using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DotNetCommon;
using DotNetCommon.Data;
using DotNetCommon.Extensions;
using System.Data.Common;
using System.Reflection;
using System.Linq.Expressions;
using System.Text;
using DBUtil.SqlSegment;
using System.ComponentModel.DataAnnotations.Schema;
using DBUtil.Builders;
using DBUtil.Attributes;
using System.Collections;
using DotNetCommon.Accessors;
using System.Text.Json;
using System.Text.Json.Nodes;
using DotNetCommon.Logger;
using System.Text.Json.Serialization.Metadata;

namespace DBUtil
{
    /// <summary>
    /// 通用数据库访问对象
    /// </summary>
    public abstract partial class DBAccess
    {
        private ILogger<DBAccess> logger = LoggerFactory.CreateLogger<DBAccess>();
        #region 构造函数
        protected DBAccess(DBType dbType, string dBConnection, string paraPrefix, bool hasSchemaDomain, DBSetting setting)
        {
            this.DBType = dbType;
            this.DBConn = dBConnection;
            this.ParaPrefix = paraPrefix;
            this.HasSchemaDomain = hasSchemaDomain;
            this.Setting = setting;
        }
        #endregion

        #region 设置Settings
        /// <summary>
        /// 设置
        /// </summary>
        public DBSetting Setting { get; private set; }
        /// <summary>
        /// 获取创建 DBAccess 时添加的配置项, 如果没有则返回 T 的默认值
        /// </summary>
        public T GetExtendSetting<T>(string key) => Setting.GetExtendSetting<T>(key);
        /// <summary>
        /// 获取创建 DBAccess 时添加的配置项, 如果没有则返回 null
        /// </summary>
        public object GetExtendSetting(string key) => Setting.GetExtendSetting(key);
        #endregion

        #region 表名或标识符分析/解析
        /// <summary>
        /// 标识符引用标记<para></para>
        /// 如 sqlserver: [],""<para></para>
        /// 如 mysql: ``,""<para></para>
        /// </summary>
        public virtual List<string> QuoteIdentifierCharacters { get; }
        /// <summary>
        /// 快速新增 Quote
        /// </summary>
        public string AddQuote(string name) => name.IsNullOrEmptyOrWhiteSpace() ? name : $"{QuoteIdentifierCharacters[0][0]}{name}{QuoteIdentifierCharacters[0][1]}";
        /// <summary>
        /// 快速去除 Quote
        /// </summary>
        protected internal string RemoveQuote(string name)
        {
            if (name.IsNullOrEmptyOrWhiteSpace()) return name;
            foreach (var item in QuoteIdentifierCharacters)
            {
                if (name.StartsWith(item[0]) && name.EndsWith(item[1]))
                {
                    return name.Substring(1, name.Length - 2);
                }
            }
            return name;
        }
        private char[][] _characters = null;
        protected virtual char[][] GetQuoteIndentifierCharacters()
        {
            if (_characters == null)
            {
                _characters = new char[QuoteIdentifierCharacters.Count][];
                for (var i = 0; i < QuoteIdentifierCharacters.Count; i++)
                {
                    _characters[i] = QuoteIdentifierCharacters[i].ToArray();
                }
            }
            return _characters;
        }

        /// <summary>
        /// 从给定的名称列表中解析出纯净的name、schema名、数据库名等, 如:
        /// <list type="bullet">
        /// <item>db.ParseObjectName("table1");</item>
        /// <item>sqlserver: db.ParseObjectName("testdb.dbo.[table1]");</item>
        /// <item>mysql: db.ParseObjectName("`testdb`.table1");</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: 传入的参数应该是到表名</remarks>
        public virtual ObjectName ParseObjectName(string name)
        {
            //基础解析
            var (db, schema, table, segCount) = AnalysisIdentifier(name);

            //构建ObjectName
            var objectName = new ObjectName()
            {
                DataBaseName = db,
                SchemaName = schema,
                Name = table,
                SegCount = segCount,
                OriginalName = name
            };

            //赋值扩展属性 DataBaseNameQuoted/SchemaNameQuoted/NameQuoted
            var firstQuote = QuoteIdentifierCharacters.FirstOrDefault()[0];
            var lastQuote = QuoteIdentifierCharacters.FirstOrDefault()[1];

            if (objectName.DataBaseName.IsNotNullOrEmptyOrWhiteSpace()) objectName.DataBaseNameQuoted = $"{firstQuote}{objectName.DataBaseName}{lastQuote}";
            else objectName.DataBaseNameQuoted = string.Empty;
            if (objectName.SchemaName.IsNotNullOrEmptyOrWhiteSpace()) objectName.SchemaNameQuoted = $"{firstQuote}{objectName.SchemaName}{lastQuote}";
            else objectName.SchemaNameQuoted = string.Empty;
            if (objectName.Name.IsNotNullOrEmptyOrWhiteSpace()) objectName.NameQuoted = $"{firstQuote}{objectName.Name}{lastQuote}";
            else objectName.NameQuoted = string.Empty;

            //赋值扩展属性 NormalName/NormalNameQuoted/FullName/FullNameQuoted
            var names = new List<string> { objectName.Name };
            if (HasSchemaDomain)
            {
                //类sqlserver的 NormalName 需要将schema名称加上
                if (objectName.SchemaName.IsNotNullOrEmptyOrWhiteSpace()) names.Add(objectName.SchemaName);
                names.Reverse();
            }
            objectName.NormalName = names.ToStringSeparated(".");
            objectName.NormalNameQuoted = names.Select(i => $"{firstQuote}{i}{lastQuote}").ToStringSeparated(".");

            if (HasSchemaDomain)
            {
                //类sqlserver的 DataBaseName 需要将db名称加上
                names.Reverse();
                if (objectName.DataBaseName.IsNotNullOrEmptyOrWhiteSpace()) names.Add(objectName.DataBaseName);
                names.Reverse();
            }
            else
            {
                //类mysql的 FullName 需要将db名称加上
                if (objectName.SchemaName.IsNotNullOrEmptyOrWhiteSpace()) names.Add(objectName.SchemaName);
                names.Reverse();
            }
            objectName.FullName = names.ToStringSeparated(".");
            objectName.FullNameQuoted = names.Select(i => $"{firstQuote}{i}{lastQuote}").ToStringSeparated(".");
            return objectName;
        }

        /// <summary>
        /// 从给定的表名或标识符中解析出纯净的 数据库、schema、标识符 名称, 如:
        /// <list type="bullet">
        /// <item>sqlserver: [testdb].[dbo].[test] => (test,dbo,testdb)</item>
        /// <item>mysql: `testdb`.`test` => (testdb,testdb,test)</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: 传入的参数应该是到表名</remarks>
        protected virtual (string dbName, string schemaName, string identityName, int segCount) AnalysisIdentifier(string fullIdentifierName)
        {
            if (fullIdentifierName.IsNullOrEmptyOrWhiteSpace()) throw new ArgumentNullException(nameof(fullIdentifierName));
            if (fullIdentifierName.Contains('\'')) throw new Exception($"不能含有危险字符 \"'\" : {fullIdentifierName}");
            fullIdentifierName = fullIdentifierName.Trim();
            if (fullIdentifierName.EndsWith('.')) throw new Exception($"错误的格式: {fullIdentifierName}");
            if (fullIdentifierName.IndexOfAny(['\r', '\n', '\t']) >= 0) throw new Exception($@"不能含有危险字符 '\r', '\n', '\t' : {fullIdentifierName}");
            var _characters = GetQuoteIndentifierCharacters();
            var arr = fullIdentifierName.ToCharArray();
            //保护模式的起始字符
            var _quoteStartArr = _characters.Select(i => i[0]).ToList();
            var _quoteEndArr = _characters.Select(i => i[1]).ToList();
            var segArr = new List<string>();
            //是否在保护模式或保护模式的结束字符
            char protect = '\0';
            var t = "";
            //刚结束完一个保护模式
            var endProtect = false;
            for (var index = 0; index < arr.Length; index++)
            {
                var c = fullIdentifierName[index];
                if (protect == '\0')
                {
                    //尚未进入保护模式([],"",``)
                    if (_quoteStartArr.IndexOf(c) >= 0)
                    {
                        //进入保护模式 不能立刻进入保护模式,比如有分隔符,如: [testdb][dbo]
                        if (endProtect) throw new Exception($"错误的格式: {fullIdentifierName}");
                        var _index = _quoteStartArr.IndexOf(c);
                        protect = _quoteEndArr[_index];
                        if (t != "") segArr.Add(t);
                        t = "";
                        continue;
                    }
                    if (c == '.')
                    {
                        //进行拆分
                        if (endProtect)
                        {
                            // 上一个保护模式刚结束【`testdb`.`tbl`】
                            endProtect = false;
                            continue;
                        }
                        if (t == "") throw new Exception($"错误的格式: {fullIdentifierName}");
                        //非保护模式下结束seg 【testdb.tbl】
                        segArr.Add(t);
                        t = "";
                        endProtect = false;
                        continue;
                    }
                    t += c;
                }
                else
                {
                    //已经在保护模式中
                    if (c == protect)
                    {
                        //结束保护模式
                        if (t == "") throw new Exception($"错误的格式: {fullIdentifierName}");
                        segArr.Add(t);
                        t = "";
                        protect = '\0';
                        endProtect = true;
                        continue;
                    }
                    else
                    {
                        t += c;
                        continue;
                    }

                }
            }
            if (protect != '\0') throw new Exception($"错误的格式: {fullIdentifierName}");
            if (t != "") segArr.Add(t);
            if (segArr.Count == 1) return (string.Empty, string.Empty, segArr[0], 1);
            if (segArr.Count == 2)
            {
                if (HasSchemaDomain) return (string.Empty, segArr[0], segArr[1], 2);
                else return (segArr[0], segArr[0], segArr[1], 2);
            }
            if (segArr.Count == 3 && !HasSchemaDomain) throw new Exception("类似mysql的db,不能以 testdb.testschema.testtable 表示名称!");
            if (segArr.Count == 3) return (segArr[0], segArr[1], segArr[2], 3);
            throw new Exception($"无法解析标识符:{fullIdentifierName}");
        }

        /// <summary>
        /// mysql: table1 => `table1`<br/>
        /// mysql: `testdb`.table1 => `testdb`.`table1`<br/>
        /// sqlserver: table1 => [table1]<br/>
        /// sqlserver: testdb.[dbo].table1 => [testdb].[dbo].[table1]<br/>
        /// </summary>
        public virtual string QuotedName(string name) => ParseObjectName(name).FullNameQuoted;
        #endregion

        #region 创建DataAdapter
        /// <summary>
        /// 创建DataAdapter
        /// </summary>
        /// <returns></returns>
        protected abstract DataAdapter CreateAdapter(DbCommand cmd);
        #endregion

        #region 基础属性
        /// <summary>
        /// 连接字符串
        /// </summary>
        public string DBConn { get; private set; }
        /// <summary>
        /// 数据库类型
        /// </summary>
        public DBType DBType { get; private set; }

        /// <summary>
        /// 当前数据库使用的参数的前缀符号
        /// </summary>
        public string ParaPrefix { get; private set; }

        /// <summary>
        /// 是否在数据库内有schema的区分,主要用来区分: mysql/sqlserver 两类的差别
        /// <list type="bullet">
        /// <item>mysql: false</item>
        /// <item>sqlserver: true</item>
        /// </list>
        /// </summary>
        public bool HasSchemaDomain { get; private set; }

        private string dbVersion = null;
        /// <summary>
        /// 数据库版本
        /// </summary>        
        public virtual string DBVersion
        {
            get
            {
                RefreshUserAndDbInfo().Wait();
                return dbVersion;
            }
        }

        private string dbName = null;
        /// <summary>
        /// 当前连接的数据库名称
        /// </summary>
        public virtual string DBName
        {
            get
            {
                RefreshUserAndDbInfo().Wait();
                return dbName;
            }
        }

        private string schemaName = null;
        /// <summary>
        /// 当前连接默认的Schema名称
        /// </summary>
        public virtual string SchemaName
        {
            get
            {
                RefreshUserAndDbInfo().Wait();
                return schemaName;
            }
        }

        private string userName = null;
        /// <summary>
        /// 操作当前数据库的用户名(如果是在sqlserver下,这个值表示当前数据库内的用户名而不是登录数据库使用的用户名,例如: 这里显示的是dbo而不是sa)
        /// </summary>
        public virtual string UserName
        {
            get
            {
                RefreshUserAndDbInfo().Wait();
                return userName;
            }
        }

        private string loginUserName = null;
        /// <summary>
        /// 登录数据库使用的用户名
        /// </summary>
        public virtual string LoginUserName
        {
            get
            {
                RefreshUserAndDbInfo().Wait();
                return loginUserName;
            }
        }

        /// <summary>
        /// 刷新db链接信息: DBName/DBVersion/UserName/LoginUserName
        /// </summary>
        /// <param name="force">是否强制刷新, 默认 false, 即: 如果已经缓存过则不再执行</param>
        /// <returns></returns>
        public async Task RefreshUserAndDbInfo(bool force = false)
        {
            if (!force
                && loginUserName.IsNotNullOrEmptyOrWhiteSpace()
                && userName.IsNotNullOrEmptyOrWhiteSpace()
                && dbName.IsNotNullOrEmptyOrWhiteSpace()
                && dbVersion.IsNotNullOrEmptyOrWhiteSpace()
                && schemaName.IsNotNullOrEmptyOrWhiteSpace()
                ) return;
            var sql = $"select {GetCurrentLoginUserSqlSeg()},{GetCurrentUserSqlSeg()},{GetCurrentDataBaseSqlSeg()},{GetCurrentDataBaseVersionSqlSeg()},{GetCurrentSchemaSqlSeg()}";
            await SelectDataReaderAsync(async reader =>
            {
                await reader.RawReader.ReadAsync();
                loginUserName = reader.RawReader.GetString(0);
                userName = reader.RawReader.GetString(1);
                dbName = reader.RawReader.GetString(2);
                dbVersion = reader.RawReader.GetString(3);
                schemaName = reader.RawReader.GetString(4);
            }, sql);
        }
        #endregion

        #region 一次最大插入行数
        /// <summary>
        /// 当一次插入的数据太多是,尝试分批执行, 以防db报错
        /// <list type="bullet">
        /// <item>sqlserver: 最大可插入1000行</item>
        /// <item>mysql: 无最大行数限制, 但与server的通讯包有限制(ensure 'max_allowed_packet' is greater than)</item>
        /// </list>
        /// </summary>
        public virtual int InsertRowsMaxCountPerBatch { get { return 0; } }
        #endregion

        #region 测试数据库连接
        /// <summary>
        /// 测试数据库连接
        /// </summary>
        public Result OpenTest()
        {
            try
            {
                return RunInSession(() => Result.Ok());
            }
            catch (Exception ex)
            {
                return Result.NotOk(ex?.Message);
            }
        }

        /// <summary>
        /// 测试数据库连接
        /// </summary>
        /// <returns></returns>
        public async Task<Result> OpenTestAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                return await RunInSessionAsync(async () => await Task.FromResult(Result.Ok()));
            }
            catch (Exception ex)
            {
                return Result.NotOk(ex?.Message);
            }
        }
        #endregion

        #region 防止sql注入
        /// <summary>
        /// 将传入的字符串进行转义, 支持转义的字符:
        /// <list type="number">
        /// <item>单引号: '</item>
        /// <item>双引号: "</item>
        /// <item>反斜杠: \</item>
        /// <item>回车符</item>
        /// <item>换行符</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: 
        /// <list type="bullet">
        /// <item>如果要对表名、列名防注入,需要使用 <seealso cref="QuotedName(string)"/></item>
        /// <item>不要进行二次调用</item>
        /// <item>不会对 % 和 _ 进行转义, 因为其在like和非like模式下表现的不一致</item>
        /// </list>
        /// </remarks>
        public virtual string EscapeString(string value, bool dealQuote, bool dealDoubleQuote)
        {
            if (value.IsNullOrEmptyOrWhiteSpace()) return value;
            StringBuilder sb = null;
            int last = -1;
            for (int i = 0; i < value.Length; i++)
            {
                char c = value[i];
                switch (c)
                {
                    /**
                     * 为什么不对 % 和 _ 进行转义?
                     * 因为只有在 like 下 【\%】才等同于 【%】, 而其他时候 【\%】不会认为是转义的效果
                     * 所以在like时特殊处理, 如: select replace(replace('%_%','_','\_'),'%','\%')
                     */
                    case '\'':
                    case '"':
                    case '\\':
                    case '\r':
                    case '\n':
                        {
                            if (c == '\'' && !dealQuote) continue;
                            if (c == '\"' && !dealDoubleQuote) continue;
                            sb ??= new StringBuilder();
                            sb.Append(value, last + 1, i - (last + 1));
                            if (c == '\r')
                            {
                                sb.Append("\\r");
                            }
                            else if (c == '\n')
                            {
                                sb.Append("\\n");
                            }
                            else
                            {
                                sb.Append('\\').Append(c);
                            }
                            last = i;
                            break;
                        }
                    default:
                        continue;
                }
            }
            sb?.Append(value, last + 1, value.Length - (last + 1));
            return sb?.ToString() ?? value;
        }

        /// <summary>
        /// 将传入的字符串进行转义, 支持转义的字符:
        /// <list type="number">
        /// <item>单引号: '</item>
        /// <item>双引号: "</item>
        /// <item>反斜杠: \</item>
        /// <item>回车符</item>
        /// <item>换行符</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: 
        /// <list type="bullet">
        /// <item>如果要对表名、列名防注入,需要使用 <seealso cref="QuotedName(string)"/></item>
        /// <item>不要进行二次调用</item>
        /// <item>不会对 % 和 _ 进行转义, 因为其在like和非like模式下表现的不一致</item>
        /// </list>
        /// </remarks>
        public virtual string EscapeString(string value)
            => EscapeString(value, true, true);


        /// <summary>
        /// 保护属性名称(在外层包裹双引号,里面的内容进行转义),如: 
        /// <list type="bullet">
        /// <item>【12abc】 => 【"12abc"】</item>
        /// <item>【"abc】 => 【"\"abc"】</item>
        /// <item>【a bc】 => 【"a bc"】</item>
        /// </list>
        /// </summary>
        /// <param name="memberName">属性名称</param>
        public string ProtectPropertyName(string memberName)
        {
            if (memberName == null) return memberName;
            return $"\"{EscapeString(memberName, false, true)}\"";
        }

        /// <summary>
        /// 保护普通字符串(在外层包裹单引号,里面的内容进行转义),如: 
        /// <list type="bullet">
        /// <item>【12abc】 => 【'12abc'】</item>
        /// <item>【"abc】 => 【'\"abc'】</item>
        /// <item>【a 'bc】 => 【'a \'bc'】</item>
        /// </list>
        /// </summary>
        /// <param name="str">字符串</param>
        public string ProtectString(string str)
        {
            if (str == null) return str;
            return $"\'{EscapeString(str, true, false)}\'";
        }

        /// <summary>
        /// 根据属性名称得到jsonpath, 如:
        /// <list type="bullet">
        /// <item>【name】 => 【'$."name"'】</item>
        /// <item>【2yk pk】 => 【'$."2yk pk"'】</item>
        /// <item>【a回车换行@pp"'w】 => 【'$."a\\r\\n@pp\\"\'w"'】</item>
        /// </list>
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public virtual string GetJsonPathWrapByPropertyName(string propName)
        {
            return ProtectString($"$.{ProtectPropertyName(propName)}");
        }
        /// <summary>
        /// 根据属性名称得到jsonpath, 如:
        /// <list type="bullet">
        /// <item>【name】 => 【'$."name"'】</item>
        /// <item>【2yk pk】 => 【'$."2yk pk"'】</item>
        /// <item>【a回车换行@pp"'w】 => 【'$."a\\r\\n@pp\\"\'w"'】</item>
        /// </list>
        /// </summary>
        /// <param name="propNames"></param>
        /// <returns></returns>
        public virtual string GetJsonPathWrapByPropertyName(IEnumerable<string> propNames)
        {
            var str = propNames.Where(i => i.IsNotNullOrEmptyOrWhiteSpace()).Select(i => ProtectPropertyName(i)).ToStringSeparated(".");
            return ProtectString($"$.{str}");
        }
        #endregion

        #region CreatePara 创建参数
        /// <summary>
        /// 创建参数
        /// </summary>
        public abstract DbParameter CreatePara();

        /// <summary>
        /// 创建参数
        /// </summary>
        /// <param name="name">参数名称</param>
        /// <param name="value">参数值</param>
        /// <param name="direction"></param>
        /// <param name="dbType"></param>
        /// <param name="size"></param>
        /// <param name="precision"></param>
        /// <param name="scale"></param>
        public virtual DbParameter CreatePara(string name, object value,
            ParameterDirection? direction = null,
            DbType? dbType = null, int? size = null,
            byte? precision = null, byte? scale = null)
        {
            var para = CreatePara();
            para.ParameterName = name;
            para.Value = value;
            if (direction != null) para.Direction = direction.Value;
            if (dbType != null) para.DbType = dbType.Value;
            if (size != null) para.Size = size.Value;
            if (precision != null) para.Precision = precision.Value;
            if (scale != null) para.Scale = scale.Value;
            return para;
        }
        #endregion

        #region 获取此实例内的参数标记前缀(mysql变量64个字符,64-12=52, 够用)
        private uint paraPrefix = 0;
        protected virtual string GetCounter()
            //当 paraPrefix 超过最大值时会默认
            => Interlocked.Increment(ref paraPrefix).ToString();
        #endregion

        #region 获取当前db/schema/user的sqlseg
        /// <summary>
        /// 获取当前schema名的sql,如:
        /// <list type="bullet">
        /// <item>MySql: "database()", 同: <seealso cref="GetCurrentDataBaseSqlSeg"/></item>
        /// <item>SqlServer: SCHEMA_NAME()</item>
        /// </list>
        /// </summary>
        /// <returns></returns>
        public abstract string GetCurrentSchemaSqlSeg();

        /// <summary>
        /// 获取当前数据库名的sql,如:
        /// <list type="bullet">
        /// <item>MySql: "database()",同: <seealso cref="GetCurrentSchemaSqlSeg"/></item>
        /// <item>SqlServer: "DB_NAME()"</item>
        /// </list>
        /// </summary>
        /// <returns></returns>
        public abstract string GetCurrentDataBaseSqlSeg();

        /// <summary>
        /// 获取当前用户名的sql,如:
        /// <list type="bullet">
        /// <item>MySql: "user()", 注意: user()返回的格式为: username@ip,如果想返回纯净用户名,传参isPure=true. 效果同: <seealso cref="GetCurrentLoginUserSqlSeg"/></item>
        /// <item>SqlServer: "CURRENT_USER", 注意: 此处返回的是 dbo, 如果想返回 sa,需要使用 "SYSTEM_USER"(<seealso cref="GetCurrentLoginUserSqlSeg"/>)</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: 因为Mysql的 select user() 返回的用户名如: <c>root@localhost</c> ,所以增加参数 <c>isPure</c> ,默认为false, 当设为true时,则返回不带ip后缀的用户名,如: <c>root</c> </remarks>
        public abstract string GetCurrentUserSqlSeg(bool isPure = false);

        /// <summary>
        /// 获取当前用户名的sql,如:
        /// <list type="bullet">
        /// <item>MySql: "user()", 注意: user()返回的格式为: username@ip,如果想返回纯净用户名,传参isPure=true. 效果同: <seealso cref="GetCurrentUserSqlSeg"/></item>
        /// <item>SqlServer: "SYSTEM_USER", 注意: 此处返回的是 sa, 如果想返回 dbo,需要使用 "CURRENT_USER"(<seealso cref="GetCurrentUserSqlSeg"/>)</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: 因为Mysql的 select user() 返回的用户名如: <c>root@localhost</c> ,所以增加参数 <c>isPure</c> ,默认为false, 当设为true时,则返回不带ip后缀的用户名,如: <c>root</c> </remarks>
        public abstract string GetCurrentLoginUserSqlSeg(bool isPure = false);

        /// <summary>
        /// 获取当前数据库实例版本的sql,如:
        /// <list type="bullet">
        /// <item>MySql: "version()"</item>
        /// <item>SqlServer: "@@VERSION"</item>
        /// </list>
        /// </summary>
        /// <returns></returns>
        public abstract string GetCurrentDataBaseVersionSqlSeg();
        #endregion

        #region GetDefaultDbType
        public abstract string GetDefaultDbType(Type type);
        #endregion

        #region 使用SqlBulkCopy批量插入数据
        /// <summary>
        /// 快速插入数据到数据库,对于不支持 BulkCopy 的将改为 insert 执行
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="tableName">表名,如果为空则使用 dt.TableName</param>
        /// <param name="timeoutSeconds">超时描述,默认半小时</param>
        /// <param name="notifyAfter">拷贝多少行后回调通知</param>
        /// <param name="callBack">进度回调,返回false可中断拷贝, 一般返回true</param>
        public abstract void BulkCopy(DataTable dt, string tableName = null, int timeoutSeconds = 60 * 30, int notifyAfter = 0, Func<long, bool> callBack = null);
        #endregion

        #region (异步)使用SqlBulkCopy批量插入数据
        /// <summary>
        /// 快速插入数据到数据库,对于不支持 BulkCopy 的将改为 insert 执行
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="tableName">表名,如果为空则使用 dt.TableName</param>
        /// <param name="timeoutSeconds">超时描述,默认半小时</param>
        /// <param name="notifyAfter">拷贝多少行后回调通知</param>
        /// <param name="callBack">进度回调,返回false可中断拷贝, 一般返回true</param>
        /// <param name="cancellationToken"></param>
        public abstract Task BulkCopyAsync(DataTable dt, string tableName = null, int timeoutSeconds = 60 * 30, int notifyAfter = 0, Func<long, Task<bool>> callBack = null, CancellationToken cancellationToken = default);
        #endregion

        #region 生成分页查询语句: GetSqlForPageSize
        /// <summary>
        /// 生成分页查询语句
        /// </summary>
        /// <param name="selectSql">select子句,如:select id,name from person where age>10</param>
        /// <param name="orderSql">排序子句,必须指定,如:order by id</param>
        /// <param name="pageSize">分页大小,如:10</param>
        /// <param name="pageIndex">当前页码,如:1</param>
        /// <returns>返回示例(mysql):select id,name from person where age>10 order by id limit 0,10</returns>
        public abstract string GetSqlForPageSize(string selectSql, string orderSql, int pageSize, int pageIndex);
        #endregion

        #region Is系列: 判断表/视图/列/存储过程/触发器是否存在
        /// <summary>
        /// 给当前查询的sql语句加上schema或db的限制条件,示例:
        /// <code>
        /// //mysql
        /// string sql = "select count(1) from INFORMATION_SCHEMA.COLUMNS t where t.TABLE_NAME= 't_user'";
        /// sql = db.AddSchemaOrDbLimit(sql, db.ParseObjectName("`testdb`.t_user"));
        /// //out:
        /// select count(1) from INFORMATION_SCHEMA.COLUMNS t where t.TABLE_NAME= 't_user' and t.TABLE_SCHEMA='testdb'
        /// </code>
        /// </summary>
        protected string AddSchemaOrDbLimit(string sql, ObjectName objName, string prefix = "t")
        {
            if (HasSchemaDomain)
            {
                //类sqlserver, schema和db是不同的概念
                if (objName.SegCount == 3) sql += $"\r\n and {prefix}.TABLE_CATALOG='{objName.DataBaseName}'";
                else sql += $"\r\n and {prefix}.TABLE_CATALOG={GetCurrentDataBaseSqlSeg()}";
                if (objName.SegCount >= 2) sql += $"\r\n and {prefix}.TABLE_SCHEMA='{objName.SchemaName}'";
                else sql += $"\r\n and {prefix}.TABLE_SCHEMA={GetCurrentSchemaSqlSeg()}";
            }
            else
            {
                //类mysql, schema和db一个意思
                if (objName.SegCount >= 2) sql += $"\r\n and {prefix}.TABLE_SCHEMA='{objName.SchemaName}'";
                else sql += $"\r\n and {prefix}.TABLE_SCHEMA={GetCurrentSchemaSqlSeg()}";
            }
            return sql;
        }
        private string IsColumnExistInTableOrViewSql(string tableOrViewName, string columnName)
        {
            tableOrViewName = EscapeString(tableOrViewName);
            columnName = EscapeString(columnName);
            var objName = ParseObjectName(tableOrViewName);
            columnName = ParseObjectName(columnName).Name;
            string sql = $@"select count(1)
from INFORMATION_SCHEMA.COLUMNS t
where
    t.TABLE_NAME='{objName.Name}'
    and
    t.COLUMN_NAME='{columnName}'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }
        /// <summary>
        /// 判断指定表或视图中是否有某一列
        /// </summary>
        /// <param name="tableOrViewName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        /// <param name="columnName">列名</param>
        public virtual bool IsColumnExistInTableOrView(string tableOrViewName, string columnName)
        {
            var sql = IsColumnExistInTableOrViewSql(tableOrViewName, columnName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        private string IsColumnExistInTableSql(string tableName, string columnName)
        {
            tableName = EscapeString(tableName);
            columnName = EscapeString(columnName);
            var objName = ParseObjectName(tableName);
            columnName = ParseObjectName(columnName).Name;

            string sql = $@"select count(1)
from INFORMATION_SCHEMA.COLUMNS c
    left join INFORMATION_SCHEMA.tables t
    on c.TABLE_CATALOG=t.TABLE_CATALOG and c.TABLE_SCHEMA=t.TABLE_SCHEMA and c.TABLE_NAME=t.TABLE_NAME
where
    c.COLUMN_NAME='{columnName}'
    and
    c.TABLE_NAME='{objName.Name}'
    and t.TABLE_TYPE='BASE TABLE'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }
        /// <summary>
        /// 判断指定表中是否有某一列
        /// </summary>
        /// <param name="tableName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        /// <param name="columnName">列名</param>
        public virtual bool IsColumnExistInTable(string tableName, string columnName)
        {
            var sql = IsColumnExistInTableSql(tableName, columnName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        private string IsColumnExistInViewSql(string viewName, string columnName)
        {
            viewName = EscapeString(viewName);
            columnName = EscapeString(columnName);
            var objName = ParseObjectName(viewName);
            columnName = ParseObjectName(columnName).Name;

            string sql = $@"select count(1)
from INFORMATION_SCHEMA.COLUMNS c
    left join INFORMATION_SCHEMA.tables t
    on c.TABLE_CATALOG=t.TABLE_CATALOG and c.TABLE_SCHEMA=t.TABLE_SCHEMA and c.TABLE_NAME=t.TABLE_NAME
where
    c.COLUMN_NAME='{columnName}'
    and
    c.TABLE_NAME='{objName.Name}'
    and t.TABLE_TYPE='VIEW'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }

        /// <summary>
        /// 判断指定视图中是否有某一列
        /// </summary>
        /// <param name="viewName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        /// <param name="columnName">列名</param>
        /// <returns>返回列是否存在</returns>
        public virtual bool IsColumnExistInView(string viewName, string columnName)
        {
            var sql = IsColumnExistInViewSql(viewName, columnName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        private string IsTableOrViewExistSql(string tableOrViewName)
        {
            tableOrViewName = EscapeString(tableOrViewName);
            var objName = ParseObjectName(tableOrViewName);

            string sql = $"select count(1) from INFORMATION_SCHEMA.TABLES t where t.TABLE_NAME='{objName.Name}'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }

        /// <summary>
        /// 判断表或视图是否存在
        /// </summary>
        /// <param name="tableOrViewName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        /// <returns>返回表或视图是否存在</returns>
        public virtual bool IsTableOrViewExist(string tableOrViewName)
        {
            var sql = IsTableOrViewExistSql(tableOrViewName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        private string IsTableExistSql(string tableName)
        {
            tableName = EscapeString(tableName);
            var objName = ParseObjectName(tableName);

            string sql = $"select count(1) from INFORMATION_SCHEMA.TABLES t where t.TABLE_NAME='{objName.Name}' and t.TABLE_TYPE='BASE TABLE'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }

        /// <summary>
        /// 判断表是否存在
        /// </summary>
        /// <param name="tableName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        /// <returns>返回表是否存在</returns>
        public virtual bool IsTableExist(string tableName)
        {
            var sql = IsTableExistSql(tableName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        private string IsViewExistSql(string viewName)
        {
            viewName = EscapeString(viewName);
            var objName = ParseObjectName(viewName);

            string sql = $"select count(1) from INFORMATION_SCHEMA.TABLES t where t.TABLE_NAME='{objName.Name}' and t.TABLE_TYPE='VIEW'";
            sql = AddSchemaOrDbLimit(sql, objName);
            return sql;
        }

        /// <summary>
        /// 判断视图是否存在
        /// </summary>
        /// <param name="viewName">
        /// 自动识别如下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.testtbl</item>
        /// <item>testdb.testtbl</item>
        /// <item>testtbl</item>
        /// </list>
        /// </param>
        public virtual bool IsViewExist(string viewName)
        {
            var sql = IsViewExistSql(viewName);
            var r = SelectScalar<int>(sql);
            return r > 0;
        }

        /// <summary>
        /// 判断存储过程是否存在, procName兼容以下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.usp_proc1</item>
        /// <item>testdb.usp_proc1</item>
        /// <item>usp_proc1</item>
        /// </list>
        /// </summary>
        public abstract bool IsProcedureExist(string procName);

        /// <summary>
        /// 判断触发器是否存在, triggerName兼容以下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.triname</item>
        /// <item>testdb.triname</item>
        /// <item>triname</item>
        /// </list>
        /// </summary>
        public abstract bool IsTriggerExist(string triggerName);
        #endregion

        #region (异步)Is系列: 判断表/视图/列/存储过程/触发器是否存在
        /// <summary>
        /// 判断指定表或视图中是否有某一列
        /// </summary>
        /// <param name="tableOrViewName">表或视图名</param>
        /// <param name="columnName">列名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回列是否存在</returns>
        public async virtual Task<bool> IsColumnExistInTableOrViewAsync(string tableOrViewName, string columnName, CancellationToken cancellationToken = default)
        {
            var sql = IsColumnExistInTableOrViewSql(tableOrViewName, columnName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断指定表中是否有某一列
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="columnName">列名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回列是否存在</returns>
        public async virtual Task<bool> IsColumnExistInTableAsync(string tableName, string columnName, CancellationToken cancellationToken = default)
        {
            var sql = IsColumnExistInTableSql(tableName, columnName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断指定视图中是否有某一列
        /// </summary>
        /// <param name="viewName">视图名</param>
        /// <param name="columnName">列名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回列是否存在</returns>
        public async virtual Task<bool> IsColumnExistInViewAsync(string viewName, string columnName, CancellationToken cancellationToken = default)
        {
            var sql = IsColumnExistInViewSql(viewName, columnName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断表或视图是否存在
        /// </summary>
        /// <param name="tableOrViewName">表或视图名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回表或视图是否存在</returns>
        public async virtual Task<bool> IsTableOrViewExistAsync(string tableOrViewName, CancellationToken cancellationToken = default)
        {
            var sql = IsTableOrViewExistSql(tableOrViewName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断表是否存在
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回表是否存在</returns>
        public async virtual Task<bool> IsTableExistAsync(string tableName, CancellationToken cancellationToken = default)
        {
            var sql = IsTableExistSql(tableName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断视图是否存在
        /// </summary>
        /// <param name="viewName">视图名</param>
        /// <param name="cancellationToken"></param>
        /// <returns>返回视图是否存在</returns>
        public async virtual Task<bool> IsViewExistAsync(string viewName, CancellationToken cancellationToken = default)
        {
            var sql = IsViewExistSql(viewName);
            var r = await SelectScalarAsync<int>(sql, cancellationToken);
            return r > 0;
        }

        /// <summary>
        /// 判断存储过程是否存在, procName兼容以下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.usp_proc1</item>
        /// <item>testdb.usp_proc1</item>
        /// <item>usp_proc1</item>
        /// </list>
        /// </summary>
        public abstract Task<bool> IsProcedureExistAsync(string procName, CancellationToken cancellationToken = default);

        /// <summary>
        /// 判断触发器是否存在, triggerName兼容以下格式:
        /// <list type="bullet">
        /// <item>testdb.dbo.triname</item>
        /// <item>testdb.triname</item>
        /// <item>triname</item>
        /// </list>
        /// </summary>
        public abstract Task<bool> IsTriggerExistAsync(string triggerName, CancellationToken cancellationToken = default);
        #endregion

        #region Id和流水号生成控制器
        public long NewId(string tableName, string colName)
            => Setting.Generator.NewId(this, tableName, colName);
        public long[] NewIds(string tableName, string colName, int count)
            => Setting.Generator.NewIds(this, tableName, colName, count);

        public async Task<long> NewIdAsync(string tableName, string colName, CancellationToken cancellationToken = default)
            => await Setting.Generator.NewIdAsync(this, tableName, colName, cancellationToken);
        public async Task<long[]> NewIdsAsync(string tableName, string colName, int count, CancellationToken cancellationToken = default)
            => await Setting.Generator.NewIdsAsync(this, tableName, colName, count, cancellationToken);

        public string NewSNO(string tableName, string colName, SerialFormat serialFormat)
            => Setting.Generator.NewSNO(this, tableName, colName, serialFormat);
        public string[] NewSNOs(string tableName, string colName, SerialFormat serialFormat, int count)
            => Setting.Generator.NewSNOs(this, tableName, colName, serialFormat, count);

        public async Task<string> NewSNOAsync(string tableName, string colName, SerialFormat serialFormat, CancellationToken cancellationToken = default)
            => await Setting.Generator.NewSNOAsync(this, tableName, colName, serialFormat, cancellationToken);
        public async Task<string[]> NewSNOsAsync(string tableName, string colName, SerialFormat serialFormat, int count, CancellationToken cancellationToken = default)
             => await Setting.Generator.NewSNOsAsync(this, tableName, colName, serialFormat, count, cancellationToken);
        #endregion

        #region 分布式锁 RunInLock
        /// <summary>
        /// 在数据库的分布式锁内运行，示例：<br/><br/>
        /// <code>
        /// RunInLock("keystring", () => { /*do something*/});
        /// </code>
        /// </summary>
        /// <remarks>
        /// 实现原理:
        /// <list type="number">
        /// <item>sqlserver: sp_getapplock</item>
        /// <item>mysql: get_lock</item>
        /// </list>
        /// </remarks>
        public void RunInLock(string lock_str, Action action, int getLockTimeoutSecond = 60 * 3)
        {
            Ensure.NotNull(lock_str, nameof(lock_str));
            Ensure.NotNull(action, nameof(action));
            Setting.Locker.RunInLock(this, lock_str, action, getLockTimeoutSecond);
        }

        /// <summary>
        /// 在数据库的分布式锁内运行，示例：<br/><br/>
        /// <code>
        /// RunInLock("keystring", () => { /*do something*/ return new { id = 1, name = "小明" }; });
        /// </code>
        /// </summary>
        /// <remarks>
        /// 实现原理:
        /// <list type="number">
        /// <item>sqlserver: sp_getapplock</item>
        /// <item>mysql: get_lock</item>
        /// </list>
        /// </remarks>
        public T RunInLock<T>(string lock_str, Func<T> func, int getLockTimeoutSecond = 60 * 3)
        {
            Ensure.NotNull(lock_str, nameof(lock_str));
            Ensure.NotNull(func, nameof(func));
            return Setting.Locker.RunInLock<T>(this, lock_str, func, getLockTimeoutSecond);
        }

        /// <summary>
        /// 在数据库的分布式锁内运行，示例：<br/><br/>
        /// <code>
        /// await RunInLockAsync("keystring", async () => { /*do something*/});
        /// </code>
        /// </summary>
        /// <remarks>
        /// 实现原理:
        /// <list type="number">
        /// <item>sqlserver: sp_getapplock</item>
        /// <item>mysql: get_lock</item>
        /// </list>
        /// </remarks>
        public async Task RunInLockAsync(string lock_str, Func<Task> func, int getLockTimeoutSecond = 60 * 3)
        {
            Ensure.NotNull(lock_str, nameof(lock_str));
            Ensure.NotNull(func, nameof(func));
            await Setting.Locker.RunInLockAsync(this, lock_str, func, getLockTimeoutSecond);
        }

        /// <summary>
        /// 在数据库的分布式锁内运行，示例：<br/><br/>
        /// <code>
        /// await RunInLockAsync("keystring", async () => { /*do something*/ return new { id = 1, name = "小明" }; });
        /// </code>
        /// </summary>
        /// <remarks>
        /// 实现原理:
        /// <list type="number">
        /// <item>sqlserver: sp_getapplock</item>
        /// <item>mysql: get_lock</item>
        /// </list>
        /// </remarks>
        public async Task<T> RunInLockAsync<T>(string lock_str, Func<Task<T>> func, int getLockTimeoutSecond = 60 * 3)
        {
            Ensure.NotNull(lock_str, nameof(lock_str));
            Ensure.NotNull(func, nameof(func));
            return await Setting.Locker.RunInLockAsync<T>(this, lock_str, func, getLockTimeoutSecond);
        }
        #endregion

        #region 数据库管理对象 Manage
        /// <summary>
        /// 数据库管理对象
        /// </summary>
        public abstract DBManage Manage { get; }
        #endregion        

        #region 运算符
        private DateTimeSqlSegment dateTimeSqlSegment = null;
        protected abstract DateTimeSqlSegment GetDateTimeSqlSegment();
        public virtual DateTimeSqlSegment DateTimeSqlSegment
        {
            get
            {
                if (dateTimeSqlSegment != null) return dateTimeSqlSegment;
                dateTimeSqlSegment = GetDateTimeSqlSegment();
                return dateTimeSqlSegment;
            }
        }

        private StringSqlSegment stringSqlSegment = null;
        protected abstract StringSqlSegment GetStringSqlSegment();
        /// <summary>
        /// 数据库字符串运算符，示例：
        /// <list type="number">
        /// <item>sqlserver: GetLength("name",true) => "len(ISNULL(null,''))"</item>
        /// <item>sqlserver: AfterString("name","-") => "SUBSTRING(name,(charindex('-',name)+len('-')),LEN(name))"</item>
        /// </list>
        /// </summary>
        public virtual StringSqlSegment StringSqlSegment
        {
            get
            {
                if (stringSqlSegment != null) return stringSqlSegment;
                stringSqlSegment = GetStringSqlSegment();
                return stringSqlSegment;
            }
        }

        private ConvertSqlSegment convertSqlSegment = null;
        protected abstract ConvertSqlSegment GetConvertSqlSegment();

        public virtual ConvertSqlSegment ConvertSqlSegment
        {
            get
            {
                if (convertSqlSegment != null) return convertSqlSegment;
                convertSqlSegment = GetConvertSqlSegment();
                return ConvertSqlSegment;
            }
        }
        #endregion

        #region 获取刚插入的自增id的Sql语句
        /// <summary>
        /// 获取刚插入的自增id(插入的最后一行的)的Sql语句，原理:
        /// <list type="number">
        /// <item>sqlserver: scope_identity()</item>
        /// <item>mysql: last_insert_id()+{len - 1}</item>
        /// <item>oracle: 只能用序列实现</item>
        /// <item>postgresql: 可以使用serial定义列类型，但实际上也是序列</item>
        /// <item>sqlite: last_insert_rowid()</item>
        /// </list>
        /// 参数: len: 插入的行数,mysql中当一次 insert 多行时, last_insert_id() 返回的是第一行的值
        /// </summary>
        /// <remarks>
        /// 注意: 关于参数len: 因为在mysql中当一次 insert 多行时, last_insert_id() 返回的是第一行的值, 所以用len表示一次插入的行数, 这样返回结果保证是最后一行的id
        /// </remarks>
        public abstract string GetLastInsertedIdSeg(int len = 1);
        #endregion

        #region 判断是否是简单类型
        private static List<Type> simpleTypes = [
            typeof(byte),typeof(sbyte),typeof(byte?),typeof(sbyte?),
            typeof(short),typeof(ushort),typeof(short?),typeof(ushort?),
            typeof(int),typeof(uint),typeof(int?),typeof(uint?),
            typeof(long),typeof(ulong),typeof(long?),typeof(ulong?),
            typeof(float),typeof(float?),
            typeof(double),typeof(double?),
            typeof(decimal),typeof(decimal?),
            typeof(char),typeof(char?),
            typeof(bool),typeof(bool?),
            typeof(string),
            typeof(Guid),typeof(Guid?),
            typeof(DateTime),typeof(DateTime?),
            typeof(DateTimeOffset),typeof(DateTimeOffset?),
            typeof(DateOnly),typeof(DateOnly?),
            typeof(TimeOnly),typeof(TimeOnly?),
            typeof(TimeSpan),typeof(TimeSpan?),
            typeof(byte[]),
        ];
        public static bool IsSimple(Type type) => simpleTypes.Contains(type);
        #endregion

        #region reader转model的编译表达式
        private static readonly ConcurrentDictionary<(string readerCode, Type type), object> cacheDictionary = [];
        private static Func<DbDataReader, object> GetFuncSimple() => reader => reader.GetValue(0);
        public Func<DbDataReader, T> GetReaderDeserialization<T>(DbDataReader reader)
        {
            var type = typeof(T);
            var func = GetReaderDeserialization(reader, type);
            return reader => func(reader).To<T>();
        }

        public Func<DbDataReader, object> GetReaderDeserialization(Type type, DbDataReader reader)
        {
            var func = GetReaderDeserialization(reader, type);
            return reader => func(reader);
        }

        private static readonly MethodInfo m_GetFieldType = null;
        private static readonly MethodInfo m_GetValue = null;
        private static readonly MethodInfo m_IsDBNull = null;
        private static readonly MethodInfo m_GetByte = null;
        private static readonly MethodInfo m_GetDateTime = null;
        private static readonly MethodInfo m_GetDecimal = null;
        private static readonly MethodInfo m_GetDouble = null;
        private static readonly MethodInfo m_GetFloat = null;
        private static readonly MethodInfo m_GetGuid = null;
        private static readonly MethodInfo m_GetInt16 = null;
        private static readonly MethodInfo m_GetInt32 = null;
        private static readonly MethodInfo m_GetInt64 = null;
        private static readonly MethodInfo m_GetString = null;
        private static readonly MethodInfo m_GetBoolean = null;
        private static readonly MethodInfo m_GetChar = null;
        private static readonly MethodInfo m_DeserializeObject = null;
        private static readonly MethodInfo m_ToString = null;
        private static readonly MethodInfo m_To = null;

        private static readonly MethodInfo m_ConvertToSByte = null;
        private static readonly MethodInfo m_ConvertToUInt16 = null;
        private static readonly MethodInfo m_ConvertToUInt32 = null;
        private static readonly MethodInfo m_ConvertToUInt64 = null;
        static DBAccess()
        {
            var readerType = typeof(DbDataReader);
            m_GetFieldType = readerType.GetMethod("GetFieldType", [typeof(int)]);
            m_GetValue = readerType.GetMethod("GetValue", [typeof(int)]);
            m_IsDBNull = readerType.GetMethod("IsDBNull", [typeof(int)]);
            m_GetByte = readerType.GetMethod("GetByte", [typeof(int)]);
            m_GetDateTime = readerType.GetMethod("GetDateTime", [typeof(int)]);
            m_GetDecimal = readerType.GetMethod("GetDecimal", [typeof(int)]);
            m_GetDouble = readerType.GetMethod("GetDouble", [typeof(int)]);
            m_GetFloat = readerType.GetMethod("GetFloat", [typeof(int)]);
            m_GetGuid = readerType.GetMethod("GetGuid", [typeof(int)]);
            m_GetInt16 = readerType.GetMethod("GetInt16", [typeof(int)]);
            m_GetInt32 = readerType.GetMethod("GetInt32", [typeof(int)]);
            m_GetInt64 = readerType.GetMethod("GetInt64", [typeof(int)]);
            m_GetString = readerType.GetMethod("GetString", [typeof(int)]);
            m_GetBoolean = readerType.GetMethod("GetBoolean", [typeof(int)]);
            m_GetChar = readerType.GetMethod("GetChar", [typeof(int)]);
            m_DeserializeObject = typeof(System.Text.Json.JsonSerializer).GetMethod("Deserialize", [typeof(string), typeof(Type), typeof(JsonSerializerOptions)]);
            m_ToString = typeof(object).GetMethod("ToString", []);
            m_To = typeof(ObjectExtensions).GetMethod("To", [typeof(object), typeof(Type), typeof(object[])]);

            m_ConvertToSByte = typeof(Convert).GetMethod("ToSByte", [typeof(object)]);
            m_ConvertToUInt16 = typeof(Convert).GetMethod("ToUInt16", [typeof(object)]);
            m_ConvertToUInt32 = typeof(Convert).GetMethod("ToUInt32", [typeof(object)]);
            m_ConvertToUInt64 = typeof(Convert).GetMethod("ToUInt64", [typeof(object)]);
        }

        public Func<DbDataReader, object> GetReaderDeserialization(DbDataReader reader, Type type)
        {
            if (IsSimple(type)) return GetFuncSimple();
            var counter = reader.FieldCount;
            var names = new List<string>(counter);
            for (var i = 0; i < counter; i++) names.Add(reader.GetName(i));
            var readerCode = names.ToStringSeparated("#");
            var func = cacheDictionary.GetOrAdd((readerCode, type), key =>
            {
                //生成表达式
                var para = Expression.Parameter(typeof(DbDataReader), "reader");
                var variable = Expression.Variable(type, "res");
                Expression assign = null;
                var assignProps = new List<BinaryExpression>();
                var reflect = key.type.GetClassGenericFullName();

                var isValueTuple = false;
                if (reflect.Name.StartsWith("System.ValueTuple<") || reflect.Name.StartsWith("System.Tuple<"))
                {
                    //db.SelectModel<(string name, int age, DateTime birth)>
                    //db.SelectModel<Tuple<string, int, DateTime>>
                    //按构造函数的顺序即可 不用名称匹配
                    var ctorVals = new List<Expression>();
                    var ctor = key.type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());

                    //ValueTuple<T1,T2,T3,T4,T5,T6,T7,TRest> 第8个在套娃
                    //Tuple<...>也一样
                    var circles = new List<(ConstructorInfo circleCtor, List<Expression> circleCtorVals)> { (ctor, ctorVals) };
                    var index = 0;
                    while (index < names.Count)
                    {
                        Expression exp = null;
                        if (index > 0 && index % 7 == 0)
                        {
                            var circleType = reflect.GenericTypes[7].type;
                            reflect = circleType.GetClassGenericFullName();
                            var circleCtor = circleType.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());
                            var circleCtorVals = new List<Expression>();
                            circles.Add((circleCtor, circleCtorVals));
                        }
                        //超出了 如: db.SelectModel<(int id,int age)>(select id,age,name from test where id = 1);
                        if (index % 7 > reflect.GenericTypes.Count - 1) break;
                        exp = mapValue(para, names[index], reflect.GenericTypes[index % 7].type);
                        circles.LastOrDefault().circleCtorVals.Add(exp);
                        index++;
                    }
                    for (var i = circles.Count - 1; i >= 0; i--)
                    {
                        var exp = Expression.New(circles[i].circleCtor, circles[i].circleCtorVals);
                        if (i == 0) assign = Expression.Assign(variable, exp);
                        else circles[i - 1].circleCtorVals.Add(exp);
                    }
                    if (reflect.Name.StartsWith("System.ValueTuple<")) isValueTuple = true;
                }
                else
                {
                    var exp = fetchInstance(para, [], names, type);
                    assign = Expression.Assign(variable, exp);
                }

                LabelTarget labelTarget = null;
                LabelExpression labelExpression = null;
                GotoExpression gotoExpression = null;
                if (isValueTuple)
                {
                    labelTarget = Expression.Label(typeof(object));
                    var convertExpression = Expression.Convert(variable, typeof(object));
                    labelExpression = Expression.Label(labelTarget, convertExpression);
                    gotoExpression = Expression.Return(labelTarget, convertExpression, typeof(object));
                }
                else
                {
                    labelTarget = Expression.Label(type);
                    labelExpression = Expression.Label(labelTarget, variable);
                    gotoExpression = Expression.Return(labelTarget, variable, type);
                }

                var expressions = new List<Expression> { assign };
                expressions.AddRange(assignProps);
                expressions.Add(gotoExpression);
                expressions.Add(labelExpression);

                //组装表达式块,编译生成委托
                BlockExpression block = null;
                if (isValueTuple) block = Expression.Block(typeof(object), [variable], expressions);
                else block = Expression.Block(type, [variable], expressions);
                var mapperFunc = Expression.Lambda<Func<DbDataReader, object>>(block, para).Compile();
                return mapperFunc;
            }) as Func<DbDataReader, object>;
            return func;

            Expression fetchInstance(ParameterExpression para, List<string> nests, List<string> names, Type type)
            {
                if (type.IsAnonymous())
                {
                    //匿名类: .ToList(i=>new { Id=i.Id,Name=i.Name }
                    var ctorVals = new List<Expression>();
                    var ctor = type.GetConstructors().FirstOrDefault();
                    var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    var prefix = nests.ToStringSeparated(".");
                    for (var i = 0; i < props.Length; i++)
                    {
                        Expression exp = null;
                        if (DBAccess.IsSimple(props[i].PropertyType))
                        {
                            var expectName = prefix.IsNullOrEmptyOrWhiteSpace() ? props[i].Name : $"{prefix}.{props[i].Name}";
                            exp = mapValue(para, expectName, props[i].PropertyType);
                        }
                        else
                        {
                            //嵌套类
                            nests.Add(props[i].Name);
                            exp = fetchInstance(para, nests, names, props[i].PropertyType);
                            nests.RemoveAt(nests.Count - 1);
                        }
                        ctorVals.Add(exp);
                    }
                    return Expression.New(ctor, ctorVals);
                }
                else
                {
                    //属性名-列名匹配:
                    // SelectModel<TUser>("select id,name from tuser")
                    // ToList(i=>new TUser{ Id = i.Id,Name=i.Name})
                    var entityInfo = GetEntityInfoInternal(type, false);

                    //使用属性赋值
                    var ctor = type.GetConstructor([]) ?? throw new Exception($"类型 [{type.GetClassFullName()}] 必须有无参构造函数!");
                    var binds = new List<MemberBinding>();
                    var newExp = Expression.New(ctor);
                    var prefix = nests.IsNullOrEmpty() ? "" : nests.ToStringSeparated(".") + ".";
                    for (var i = 0; i < entityInfo.EntityPropertyInfos.Count; i++)
                    {
                        var expects = new List<string>();
                        var prop = entityInfo.EntityPropertyInfos[i];
                        var readerColName = string.Empty;
                        if (names.Contains($"{prefix}{prop.ColumnNamePure}"))
                        {
                            readerColName = $"{prefix}{prop.ColumnNamePure}";
                        }
                        else if (names.Contains($"{prefix}{prop.PropNamePure}"))
                        {
                            readerColName = $"{prefix}{prop.PropNamePure}";
                        }
                        else
                        {
                            var idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.ColumnNamePure}", StringComparison.OrdinalIgnoreCase));
                            if (idx < 0) idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.PropNamePure}", StringComparison.OrdinalIgnoreCase));
                            if (idx < 0) idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.ColumnNamePure.Replace('_', '\0')}", StringComparison.OrdinalIgnoreCase));
                            if (idx < 0) idx = names.FindIndex(i => string.Equals(i, $"{prefix}{prop.PropNamePure.Replace('_', '\0')}", StringComparison.OrdinalIgnoreCase));
                            if (idx < 0) continue;
                            readerColName = names[idx];
                        }
                        var exp = mapValue(para, readerColName, prop.Type);
                        binds.Add(Expression.Bind(prop.PropertyInfo, exp));
                    }
                    //嵌套属性
                    if (names.Any(i => i.Contains('.')))
                    {
                        var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(i => !DBAccess.IsSimple(i.PropertyType)).ToList();
                        foreach (var prop in props)
                        {
                            var newNests = nests.ToList().AddFluent(prop.Name);
                            var _pre = newNests.ToStringSeparated(".") + '.';
                            var newNames = names.Where(i => i.StartsWith(_pre)).ToList();
                            if (newNames.IsNotNullOrEmpty())
                            {
                                var exp = fetchInstance(para, newNests, names, prop.PropertyType);
                                if (exp != null) binds.Add(Expression.Bind(prop, exp));
                            }
                            newNests.RemoveAt(newNests.Count - 1);
                        }
                    }
                    return Expression.MemberInit(Expression.New(ctor), binds);
                }
            }

            Expression mapValue(ParameterExpression para, string colName, Type memberType)
            {
                var isNullAble = memberType.IsNullable();
                bool isEnum = isNullAble ? Nullable.GetUnderlyingType(memberType).IsEnum : memberType.IsEnum;
                var typeClassFullName = memberType.GetClassFullName();

                var index = names.IndexOf(colName);
                switch (typeClassFullName)
                {
                    #region 非 NullAble<T>
                    case "bool":
                        {
                            return Expression.Call(para, m_GetBoolean, Expression.Constant(index));
                        }
                    case "char":
                        {
                            return Expression.Call(para, m_GetChar, Expression.Constant(index));
                        }
                    case "byte":
                        {
                            return Expression.Call(para, m_GetByte, Expression.Constant(index));
                        }
                    case "sbyte":
                        {
                            return Expression.Call(m_ConvertToSByte, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                        }
                    case "System.DateTime":
                        {
                            return Expression.Call(para, m_GetDateTime, Expression.Constant(index));
                        }
                    case "System.DateTimeOffset":
                        {
                            return Expression.New(
                                    typeof(DateTimeOffset).GetConstructor(new[] { typeof(DateTime) }),
                                    Expression.Call(para, m_GetDateTime, Expression.Constant(index)));
                        }
                    case "decimal":
                        {
                            return Expression.Call(para, m_GetDecimal, Expression.Constant(index));
                        }
                    case "double":
                        {
                            return Expression.Call(para, m_GetDouble, Expression.Constant(index));
                        }
                    case "float":
                        {
                            return Expression.Call(para, m_GetFloat, Expression.Constant(index));
                        }
                    case "System.Guid":
                        {
                            return Expression.Call(para, m_GetGuid, Expression.Constant(index));
                        }
                    case "short":
                        {
                            return Expression.Call(para, m_GetInt16, Expression.Constant(index));
                        }
                    case "ushort":
                        {
                            return Expression.Call(m_ConvertToUInt16, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                        }
                    case "int":
                        {
                            return Expression.Call(para, m_GetInt32, Expression.Constant(index));
                        }
                    case "uint":
                        {
                            return Expression.Call(m_ConvertToUInt32, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                        }
                    case "long":
                        {
                            return Expression.Call(para, m_GetInt64, Expression.Constant(index));
                        }
                    case "ulong":
                        {
                            return Expression.Call(m_ConvertToUInt64, Expression.Call(para, m_GetValue, Expression.Constant(index)));
                        }
                    case "string":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetString, Expression.Constant(index)), memberType));
                        }
                    case "byte[]":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetValue, Expression.Constant(index)), memberType));
                        }
                    #endregion
                    #region NullAble<T>
                    case "bool?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetBoolean, Expression.Constant(index)), memberType));
                        }
                    case "char?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetChar, Expression.Constant(index)), memberType));
                        }
                    case "byte?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetByte, Expression.Constant(index)), memberType));
                        }
                    case "sbyte?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(m_ConvertToSByte, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                        }
                    case "System.DateTime?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetDateTime, Expression.Constant(index)), memberType));
                        }
                    case "System.DateTimeOffset?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.New(
                                        typeof(DateTimeOffset).GetConstructor(new[] { typeof(DateTime) }),
                                        Expression.Call(para, m_GetDateTime, Expression.Constant(index))), memberType));
                        }
                    case "decimal?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetDecimal, Expression.Constant(index)), memberType));
                        }
                    case "double?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType), Expression.Convert(Expression.Call(para, m_GetDouble, Expression.Constant(index)), memberType));
                        }
                    case "float?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetFloat, Expression.Constant(index)), memberType));
                        }
                    case "System.Guid?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Convert(Expression.Call(para, m_GetGuid, Expression.Constant(index)), memberType), memberType));
                        }
                    case "short?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType), memberType));
                        }
                    case "ushort?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(m_ConvertToUInt16, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                        }
                    case "int?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType));
                        }
                    case "uint?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(m_ConvertToUInt32, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                        }
                    case "long?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType));
                        }
                    case "ulong?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(m_ConvertToUInt64, Expression.Call(para, m_GetValue, Expression.Constant(index))), memberType));
                        }
                    case "string?":
                        {
                            return Expression.Condition(
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(null, memberType),
                                    Expression.Convert(Expression.Call(para, m_GetString, Expression.Constant(index)), memberType));
                        }
                    #endregion
                    default: break;
                }
                #region 枚举
                if (isEnum)
                {
                    //先判断 reader 中是否能读取为 int, 能的话直接转, 不能的话读取为 string 后, 执行 Enum.Parse()
                    //mysql 中列类型(enum,set) 默认读取是string 读取 int 会报错
                    if (isNullAble)
                    {
                        //reader.isDBNull(idx)?null:
                        //  reader.GetFieldType(idx)==typeof(int)? (EnumTest)reader.GetInt(idx):
                        //    reader.GetFieldType(idx)==typeof(long)?(EnumTest)reader.GetInt64(idx):
                        //      reader.GetFieldType(idx)==typeof(short)?(EnumTest)reader.GetInt16(idx):
                        //        (reader.GetString(idx)==""||reader.GetString(idx)=="0")?(EnumTest)0:
                        //                (EnumTest)Enum.Parse(typeof(EnumTest,reader.GetString(idx),true)
                        //            )
                        return Expression.Condition(Expression.Call(para, m_IsDBNull, Expression.Constant(index)), Expression.Constant(null, memberType),
                                         Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(int), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType),
                                                Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(long), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType),
                                                        Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(short), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType),
                                                                Expression.Condition(
                                                                    Expression.Or(Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("", typeof(string))), Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("0", typeof(string)))),
                                                                    Expression.Convert(Expression.Constant(0, typeof(int)), memberType), Expression.Convert(Expression.Call(
                                                                    typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)]), Expression.Constant(Nullable.GetUnderlyingType(memberType)), Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant(true)), memberType)
                                                                )
                                                        )
                                                )
                                         )
                               );
                    }
                    else
                    {
                        //reader.GetFieldType(idx)==typeof(int)? (EnumTest)reader.GetInt32(idx):
                        //  reader.GetFieldType(idx)==typeof(long)?(EnumTest)reader.GetInt64(idx):
                        //    reader.GetFieldType(idx)==typeof(short)?(EnumTest)reader.GetInt16(idx):
                        //      (reader.isDBNull()?null:
                        //          (reader.GetString(idx)==""||reader.GetString(idx)=="0")?(EnumTest)0:
                        //              (EnumTest)Enum.Parse(typeof(EnumTest,reader.GetString(idx),true)
                        //          )
                        //       )
                        return Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(int), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt32, Expression.Constant(index)), memberType),
                                        Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(long), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt64, Expression.Constant(index)), memberType),
                                                Expression.Condition(Expression.Equal(Expression.Call(para, m_GetFieldType, Expression.Constant(index)), Expression.Constant(typeof(short), typeof(Type))), Expression.Convert(Expression.Call(para, m_GetInt16, Expression.Constant(index)), memberType),
                                                        Expression.Condition(
                                                            Expression.Or(Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("", typeof(string))), Expression.Equal(Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant("0", typeof(string)))),
                                                            Expression.Convert(Expression.Constant(0, typeof(int)), memberType), Expression.Convert(Expression.Call(
                                                            typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)]), Expression.Constant(memberType), Expression.Call(para, m_GetString, Expression.Constant(index)), Expression.Constant(true)), memberType)
                                                         )
                                                )
                                        )
                               );
                    }
                }
                #endregion
                //reader.GetValue(index)
                var dataExp = Expression.Call(para, m_GetValue, Expression.Constant(index));

                if (memberType.IsValueType)
                    return Expression.Convert(Expression.Call(null, m_To, dataExp, Expression.Constant(memberType), Expression.Constant(null, typeof(object[]))), memberType);

                // result = isnull?null:(propType)DeserializeObject(getvalue(index).ToString(),propType)
                return Expression.Condition(
                                    //reader.IsDBNull(index)
                                    Expression.Call(para, m_IsDBNull, Expression.Constant(index)),
                                    Expression.Constant(memberType.GetDefault(), memberType),
                                    //(PropType)Newtonsoft.Json.JsonConvert.DeserializeObject(reader.GetValue(index).ToString(),propType)
                                    Expression.Convert(
                                        Expression.Call(null, m_DeserializeObject, new Expression[] {
                                                        Expression.Call(dataExp, m_ToString, new Expression[0]), Expression.Constant(memberType),Expression.Constant(null,typeof(JsonSerializerOptions)) }), memberType, null));
            }
        }
        #endregion

        #region LinkWhereFilter
        /// <summary>
        /// 将传入的 filter 转成 where语句，示例:
        /// <list type="bullet">
        /// <item>传入: "and id>10" 返回: "where 1=1 and id>10"</item>
        /// <item>传入: "id>10" 返回: "where id>10"</item>
        /// <item>传入: "" 返回: "where 1=1"</item>
        /// </list>
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public string LinkWhereFilterSeg(string filter)
        {
            if (filter.IsNullOrEmptyOrWhiteSpace()) return "where 1=1";
            filter = filter.Trim();
            var tmp = filter.ToUpper();
            if (tmp.StartsWith("AND ") || tmp.StartsWith("OR "))
            {
                return $"where 1=1 {filter}";
            }
            else
            {
                return $"where {filter}";
            }
        }
        #endregion

        #region 序列
        /// <summary>
        /// 是否支持序列
        /// </summary>
        /// <returns></returns>
        public abstract bool IsSupportSequence();

        /// <summary>
        /// 获取 下一个序列值 的sql语句
        /// </summary>
        /// <param name="name">序列名称</param>
        /// <returns></returns>
        public abstract string NextSequenceValueSeg(string name);
        #endregion

        #region EntityInfo
        public EntityInfo GetEntityInfo(Type type) => GetEntityInfoInternal(type, true);
        public EntityInfo GetEntityInfo<T>() where T : class, new() => GetEntityInfoInternal(typeof(T), true);

        private static readonly ConcurrentDictionary<string, Dictionary<Type, EntityInfo>> _caches = [];
        internal EntityInfo GetEntityInfoInternal<T>(bool needClone = false) => GetEntityInfoInternal(typeof(T), needClone);
        internal EntityInfo GetEntityInfoInternal(Type type) => GetEntityInfoInternal(type, false);
        internal EntityInfo GetEntityInfoInternal(Type type, bool needClone = false)
        {
            var dic = _caches.GetOrAdd(DBConn, _ => new Dictionary<Type, EntityInfo>());
            if (!dic.TryGetValue(type, out var entityInfo))
            {
                lock (dic)
                {
                    GenerateEntityInfo(type, dic);
                    entityInfo = dic[type];
                }
            }
            if (needClone) entityInfo = entityInfo.Clone();
            return entityInfo;
        }

        private void GenerateEntityInfo(Type type, Dictionary<Type, EntityInfo> dic)
        {
            var entityInfo = new EntityInfo();
            dic.Add(type, entityInfo);
            var attrs = type.GetCustomAttributes();
            //TableName SchemaName
            if (attrs.FirstOrDefault(i => i is TableAttribute) is TableAttribute tableAttr)
            {
                entityInfo.TableNamePure = tableAttr.Name;
                entityInfo.SchemaNamePure = tableAttr.Schema;
                if (entityInfo.SchemaNamePure.IsNotNullOrEmptyOrWhiteSpace()) entityInfo.TableNameSeg = $"{AddQuote(tableAttr.Schema)}.{AddQuote(tableAttr.Name)}";
                else entityInfo.TableNameSeg = AddQuote(entityInfo.TableNamePure);
            }
            else
            {
                //class: InnerClass<int>
                entityInfo.TableNamePure = type.Name.Split('`')[0];
                entityInfo.TableNameSeg = AddQuote(entityInfo.TableNamePure);
            }
            entityInfo.Type = type;
            entityInfo.TypeClassFullName = type.GetClassFullName();

            //properties
            var tmp = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //只读的忽略
            var props = tmp.Where(i => i.CanWrite).ToList();
            //设定: 子类会覆盖父类的同名属性, 如下面的定义将会产生两个 Id 子类的排序在前面
            //class PersonEntity : BaseEntity<int> { public new int Id { get; set; } }
            //abstract class BaseEntity<TPrimaryKey> { public virtual TPrimaryKey Id { get; set; } }
            props = props.DistinctBy(i => i.Name).ToList();
            for (int i = 0, count = props.Count; i < count; i++)
            {
                var prop = props[i];
                var propAttrs = prop.GetCustomAttributes();
                var entityPropertyInfo = new EntityPropertyInfo();
                entityPropertyInfo.PropertyInfo = prop;
                entityPropertyInfo.Type = prop.PropertyType;
                entityPropertyInfo.DefaultValue = prop.PropertyType.GetDefault();
                entityPropertyInfo.TypeClassFullName = prop.PropertyType.GetClassFullName();
                entityPropertyInfo.IsNullAble = prop.PropertyType.IsNullable();
                entityPropertyInfo.PropNamePure = prop.Name;
                entityPropertyInfo.PropNameSeg = AddQuote(prop.Name);
                var tmpType = entityPropertyInfo.Type;
                if (entityPropertyInfo.IsNullAble) tmpType = entityPropertyInfo.Type.GenericTypeArguments[0];
                entityPropertyInfo.IsEnum = tmpType.IsEnum;
                if (entityPropertyInfo.IsEnum) entityPropertyInfo.HasFlag = tmpType.GetCustomAttributes(typeof(FlagsAttribute), false).Any();
                entityInfo.EntityPropertyInfos.Add(entityPropertyInfo);

                //基本信息
                #region 基本信息               

                //基本信息
                if (simpleTypes.Contains(tmpType) || tmpType.IsEnum)
                {
                    //简单类型
                    entityPropertyInfo.IsColumn = true;
                    entityPropertyInfo.TypeCode = tmpType.GetTypeCode();
                }
                var jsonStoreAttr = propAttrs.FirstOrDefault(i => i is JsonStoreAttribute) as JsonStoreAttribute;
                if (jsonStoreAttr != null)
                {
                    entityPropertyInfo.IsColumn = true;
                    entityPropertyInfo.TypeCode = tmpType.GetTypeCode();
                    entityPropertyInfo.JsonBucket = jsonStoreAttr.Bucket;
                    entityPropertyInfo.JsonKey = jsonStoreAttr.Key;
                    entityPropertyInfo.JsonSelectKey = jsonStoreAttr.SelectKey;
                    entityPropertyInfo.IsJsonArray = ParseJsonDataType(tmpType) == EnumJsonDataType.Array;
                }
                //TODO: 既不是简单类型,也没有 [JsonStore] 属性,不能当做正常列对待
                //if (!entityPropertyInfo.IsColumn)
                //{

                //}

                //[Column]
                var colAttr = propAttrs.FirstOrDefault(i => i is ColumnAttribute) as ColumnAttribute;
                entityPropertyInfo.ColumnNamePure = colAttr?.Name ?? prop.Name;
                entityPropertyInfo.ColumnNameSeg = AddQuote(entityPropertyInfo.ColumnNamePure);
                entityPropertyInfo.TypeName = colAttr?.TypeName;
                entityPropertyInfo.IsColumnNameSegEqualPropNameSeg = entityPropertyInfo.PropNameSeg == entityPropertyInfo.ColumnNameSeg;
                // [Column] 源码中默认就是-1
                entityPropertyInfo.Order = colAttr?.Order ?? -1;
                entityPropertyInfo.IsDbString = colAttr?.TypeName?.Contains("char");
                if (entityPropertyInfo.IsDbString != true) entityPropertyInfo.IsDbString = colAttr?.TypeName?.Contains("text");
                #endregion

                //ignore
                #region Ignore
                var isNotMapped = propAttrs.Any(i => i is NotMappedAttribute);
                if (isNotMapped)
                {
                    entityPropertyInfo.IsIgnoreInsert = entityPropertyInfo.IsIgnoreUpdate = entityPropertyInfo.IsIgnoreSelect = true;
                }
                else
                {
                    var isIgnoreWrite = propAttrs.Any(i => i is IgnoreWriteAttribute);
                    if (isIgnoreWrite)
                    {
                        entityPropertyInfo.IsIgnoreInsert = entityPropertyInfo.IsIgnoreUpdate = true;
                    }
                    else
                    {
                        entityPropertyInfo.IsIgnoreUpdate = propAttrs.Any(i => i is IgnoreUpdateAttribute);
                        entityPropertyInfo.IsIgnoreInsert = propAttrs.Any(i => i is IgnoreInsertAttribute);
                        entityPropertyInfo.IsIgnoreSelect = propAttrs.Any(i => i is IgnoreSelectAttribute);
                    }
                }
                #endregion

                //主键
                #region 主键
                var primaryAttr = propAttrs.FirstOrDefault(i => i is PrimaryKeyAttribute) as PrimaryKeyAttribute;
                if (primaryAttr != null)
                {
                    entityPropertyInfo.IsPrimaryKey = true;
                    entityPropertyInfo.PrimaryKeyStrategy = primaryAttr.KeyStrategy;
                }
                else
                {
                    entityPropertyInfo.IsPrimaryKey = false;
                }
                #endregion

                //SelectFunc
                entityPropertyInfo.SelectFunc = (string alias) =>
                {
                    var ret = alias.IsNotNullOrEmptyOrWhiteSpace() ? $"{alias}.{entityPropertyInfo.ColumnNameSeg}" : entityPropertyInfo.ColumnNameSeg;
                    if (!entityPropertyInfo.IsColumnNameSegEqualPropNameSeg) ret += $" {entityPropertyInfo.PropNameSeg}";
                    return ret;
                };
                //SelectValueFunc
                entityPropertyInfo.SelectValueFunc = alias => alias.IsNotNullOrEmptyOrWhiteSpace() ? $"{alias}.{entityPropertyInfo.ColumnNameSeg}" : $"{entityPropertyInfo.ColumnNameSeg}";
            }
            //当联合主键时 不能有自增主键
            var tmp2 = entityInfo.EntityPropertyInfos.Where(i => i.IsPrimaryKey && i.IsColumn).ToList();
            if (tmp2.Count > 1 && tmp2.Any(i => i.PrimaryKeyStrategy == KeyStrategy.Identity))
                throw new Exception($"不能将自增列{tmp2.FirstOrDefault(i => i.PrimaryKeyStrategy == KeyStrategy.Identity).ColumnNameSeg}加入到联合主键({tmp2.Select(i => i.ColumnNameSeg).ToStringSeparated(",")})!");
            if (tmp2.Count > 6) throw new Exception($"联合主键最多支持6列!");
            PostDealEntityInfo(entityInfo);
            //填充信息
        }

        protected virtual void PostDealEntityInfo(EntityInfo entityInfo) { }
        #endregion

        #region GetColumnName
        public List<string> GetColumnNames(Type type, IEnumerable<string> propNames)
        {
            Ensure.NotNull(type, nameof(type));
            Ensure.NotNullOrEmpty(propNames, nameof(propNames));
            var ent = GetEntityInfoInternal(type, false);
            var list = new List<string>(8);
            foreach (var propName in propNames)
            {
                var p = ent.EntityPropertyInfos.FirstOrDefault(i => i.PropNamePure == propName) ?? throw new Exception($"在类 {ent.TypeClassFullName} 中没有找到名为 {propName} 的属性!");
                list.Add(p.ColumnNameSeg);
            }
            return list;
        }
        public List<string> GetColumnNames<T>(IEnumerable<string> propNames) where T : class, new()
            => GetColumnNames(typeof(T), propNames);
        public string GetColumnName(Type type, string propName)
        {
            Ensure.NotNull(type, nameof(type));
            Ensure.NotNullOrEmptyOrWhiteSpace(propName, nameof(propName));
            var ent = GetEntityInfoInternal(type, false);
            var p = ent.EntityPropertyInfos.FirstOrDefault(i => i.PropNamePure == propName) ?? throw new Exception($"在类 {ent.TypeClassFullName} 中没有找到名为 {propName} 的属性!");
            return p.ColumnNameSeg;
        }
        public string GetColumnName<T>(string propName) => GetColumnName(typeof(T), propName);
        public List<string> GetColumnNames<T>(Expression<Func<T, object>> expression) where T : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var propNames = ExpressionHelper.GetInitOrReturnPropNames(expression);
            if (propNames.IsNullOrEmpty()) throw new Exception($"表达式必须精确指定属性列表, 如: i=>i.Name 或 i=>new{{i.Name}}, 而不是: i=>i 或 为空");
            return GetColumnNames(typeof(T), propNames);
        }
        public string GetColumnName<T>(Expression<Func<T, object>> expression) where T : class, new()
            => GetColumnNames<T>(expression).First();
        #endregion

        #region InsertBuilder
        public InsertBuilder Insert()
        {
            return new InsertBuilder(this, null, null);
        }
        public InsertBuilder Insert(string tableName)
        {
            return new InsertBuilder(this, tableName, null);
        }
        public InsertBuilder Insert(string tableName, Dictionary<string, object> dic)
        {
            return new InsertBuilder(this, tableName, dic);
        }
        public InsertBuilder Insert(string tableName, IEnumerable<Dictionary<string, object>> dics)
        {
            return new InsertBuilder(this, tableName, dics);
        }

        public InsertBuilder<T> Insert<T>(params T[] entities) where T : class, new()
        {
            if (typeof(T).IsAssignableTo(typeof(IDictionary))) throw new Exception($"不能在 db.Insert<T>(params T[] entities) 中传入 Dictionary, 如果想插入字典, 可以使用: db.Insert(tableName,dic)!");
            return new InsertBuilder<T>(this, entities);
        }
        public InsertBuilder<T> Insert<T>(IEnumerable<T> entities) where T : class, new()
        {
            if (typeof(T).IsAssignableTo(typeof(IDictionary))) throw new Exception($"不能在 db.Insert<T>(IEnumerable<T> entities) 中传入 Dictionary[], 如果想插入字典, 可以使用: db.Insert(tableName,dics)!");
            return new InsertBuilder<T>(this, entities);
        }
        public InsertBuilder<T> Insert<T>(List<T> entities) where T : class, new()
        {
            if (typeof(T).IsAssignableTo(typeof(IDictionary))) throw new Exception($"不能在 db.Insert<T>(List<T> entities) 中传入 Dictionary[], 如果想插入字典, 可以使用: db.Insert(tableName,dics)!");
            return new InsertBuilder<T>(this, entities);
        }
        public InsertBuilder<T> Insert<T>(IList<T> entities) where T : class, new()
        {
            if (typeof(T).IsAssignableTo(typeof(IDictionary))) throw new Exception($"不能在 db.Insert<T>(IList<T> entities) 中传入 Dictionary[], 如果想插入字典, 可以使用: db.Insert(tableName,dics)!");
            return new InsertBuilder<T>(this, entities);
        }
        public InsertBuilder<T> Insert<T>(ICollection<T> entities) where T : class, new()
        {
            if (typeof(T).IsAssignableTo(typeof(IDictionary))) throw new Exception($"不能在 db.Insert<T>(ICollection<T> entities) 中传入 Dictionary[], 如果想插入字典, 可以使用: db.Insert(tableName,dics)!");
            return new InsertBuilder<T>(this, entities);
        }
        #endregion

        #region UpdateBuilder
        public UpdateBuilder Update()
        {
            return new UpdateBuilder(this, null, null);
        }
        public UpdateBuilder Update(string tableName)
        {
            return new UpdateBuilder(this, null, tableName);
        }
        public UpdateBuilder Update(string tableName, Dictionary<string, object> dic)
        {
            return new UpdateBuilder(this, dic, tableName);
        }
        public UpdateBuilder<T> Update<T>() where T : class, new()
        {
            return new UpdateBuilder<T>(this);
        }
        #endregion

        #region DeleteBuilder
        public DeleteBuilder Delete()
        {
            return new DeleteBuilder(this, null, null);
        }
        public DeleteBuilder Delete(string tableName)
        {
            return new DeleteBuilder(this, tableName, null);
        }
        public DeleteBuilder<T> Delete<T>() where T : class, new()
            => new DeleteBuilder<T>(this, EnumDeleteBuilderType.Delete, null);

        public DeleteBuilder<T> DeleteByPrimary<T>(object primaryValue) where T : class, new()
        {
            Ensure.NotNull(primaryValue, nameof(primaryValue));
            return new DeleteBuilder<T>(this, EnumDeleteBuilderType.DeleteByPrimary, primaryValue);
        }
        #endregion

        #region SelectBuilder
        public SelectBuilder<T> Select<T>() where T : class, new()
        {
            return new SelectBuilder<T>(this, "t");
        }
        public SelectBuilder<T> Select<T>(string alias) where T : class, new()
        {
            if (alias.IsNullOrEmptyOrWhiteSpace()) alias = "t";
            return new SelectBuilder<T>(this, alias);
        }
        #endregion

        #region SaveOneBuilder
        public SaveOneBuilder<T> SaveOne<T>() where T : class, new()
        {
            return new SaveOneBuilder<T>(this);
        }
        public SaveOneBuilder<T> SaveOne<T>(T entity) where T : class, new()
        {
            return new SaveOneBuilder<T>(this, entity);
        }
        #endregion

        #region SelectTreeBuilder
        public SelectTreeBuilder<T, Dto> SelectTree<T, Dto>(Expression<Func<T, object>> idSelector, Expression<Func<T, object>> parentIdSelector, Expression<Func<Dto, IList<Dto>>> childrenSelector) where T : class, new() where Dto : class, new()
        {
            return new SelectTreeBuilder<T, Dto>(this, idSelector, parentIdSelector, childrenSelector);
        }
        public SelectTreeBuilder<T> SelectTree<T>(Expression<Func<T, object>> idSelector, Expression<Func<T, object>> parentIdSelector, Expression<Func<T, IList<T>>> childrenSelector) where T : class, new()
        {
            return new SelectTreeBuilder<T>(this, idSelector, parentIdSelector, childrenSelector);
        }

        public SelectTreeBuilder<T, Dto> SelectTree<T, Dto>(string idPropName = null, string parentIdPropName = null, string childrenPropName = null) where T : class, new() where Dto : class, new()
        {
            return new SelectTreeBuilder<T, Dto>(this, idPropName.IfNullOrEmptyUse("Id"), parentIdPropName.IfNullOrEmptyUse("ParentId"), childrenPropName.IfNullOrEmptyUse("Children"));
        }
        public SelectTreeBuilder<T> SelectTree<T>(string idPropName = null, string parentIdPropName = null, string childrenPropName = null) where T : class, new()
        {
            return new SelectTreeBuilder<T>(this, idPropName.IfNullOrEmptyUse("Id"), parentIdPropName.IfNullOrEmptyUse("ParentId"), childrenPropName.IfNullOrEmptyUse("Children"));
        }
        #endregion        

        #region 方便构建sql
        /// <summary>
        /// 使用示例:
        /// <list type="bullet">
        /// <item>db.TableSeg&lt;PersonEntity>() => "t_person"</item>
        /// <item>db.TableSeg&lt;PersonEntity>("t") => "t_person t"</item>
        /// <item>db.TableSeg&lt;PersonEntity>(alias:"t", func:(old)=>old+"_202201") => "t_person_202201 t"</item>
        /// </list>
        /// </summary>
        public string TableSeg<TEntity>(string alias = null, Func<string, string> func = null) where TEntity : class, new()
        {
            var name = GetEntityInfoInternal(typeof(TEntity), false).TableNameSeg;
            if (func != null) name = func(name);
            if (alias.IsNullOrEmptyOrWhiteSpace()) return name;
            return $"{name} {alias}";
        }

        #region ColumnSeg
        /// <summary>
        /// 和 db.ColumnSeg&lt;T>(...) 相比, 这个不返回列的别名
        /// 使用示例:
        /// <list type="bullet">
        /// <item>db.ColumnPureSeg&lt;PersonEntity>() => "id,name,age"</item>
        /// <item>db.ColumnPureSeg&lt;PersonEntity>(t=>t.Id) => "t.id"</item>
        /// <item>db.ColumnPureSeg&lt;PersonEntity>(t=>new { t.Id, t.Name}) => "t.id,t.name"</item>
        /// <item>db.ColumnPureSeg&lt;PersonEntity>(_=>new { _.Id, _.Name}) => "id,name"</item>
        /// <item>db.ColumnPureSeg&lt;PersonEntity>(t=>new { tid = t.Id, tname= t.Name}) => "id,name"</item>
        /// <item>mysql: db.ColumnPureSeg&lt;PersonEntity>(t=>new { tid = t.Id, tname= t.Name.SubString(0,2)}) => "id,substr(name,0,2) `tname`"</item>
        /// </list>
        /// </summary>
        public string ColumnPureSeg<TEntity>(Expression<Func<TEntity, object>> expression = null) where TEntity : class, new()
        {
            var entity = GetEntityInfoInternal(typeof(TEntity), false);
            if (expression == null || expression.Body == expression.Parameters.FirstOrDefault())
            {
                //ColumnSeg<UserEntity>() 或 ColumnSeg<UserEntity>(i=>i)
                var alias = expression?.Parameters.FirstOrDefault()?.Name;
                return entity.EntityPropertyInfos
                    .Where(i => i.IsColumn && !i.IsIgnoreSelect)
                    .OrderBy(i => i.Order)
                    .Select(i => i.SelectValueFunc((alias.IsNullOrEmptyOrWhiteSpace() || alias == "_") ? "" : alias))
                    .ToStringSeparated(",");
            }
            var (exp, midValues) = ExpressionHelper.ReduceLambda(expression);
            if (exp is LambdaExpression lambda)
            {
                if (lambda.Body.NodeType == ExpressionType.New)
                {
                    //ColumnSeg<UserEntity>(i=>new{})
                    var newExp = lambda.Body as NewExpression;
                    var args = newExp.Arguments;//Id=i.Id 中的 i.Id
                    var members = newExp.Members;//Id=i.Id 中的 Id=
                    var list = new List<string>();
                    for (int i = 0; i < args.Count; i++)
                    {
                        var arg = args[i];
                        var _val = BuilderHelper.ParseSql(arg, this, parameters: expression.Parameters, midValues: midValues);
                        if (arg.NodeType == ExpressionType.MemberAccess) list.Add(_val);
                        else list.Add($"{_val} {this.AddQuote(members[i].Name)}");
                    }
                    return list.ToStringSeparated(",");
                }
                if (lambda.Body.NodeType == ExpressionType.MemberInit)
                {
                    //Select(i=>new Dto{ Id = i.Id, Name = i.Name })
                    var initExp = lambda.Body as MemberInitExpression;
                    var type = initExp.Type;
                    var entityInfo = this.GetEntityInfoInternal(type);

                    var bindings = initExp.Bindings;
                    var list = new List<string>();
                    for (int i = 0; i < bindings.Count; i++)
                    {
                        var binding = bindings[i];
                        //expr中的这个属性可能是常量
                        var assign = binding as MemberAssignment;
                        var exp2 = assign.Expression;
                        var _val = BuilderHelper.ParseSql(exp2, this, parameters: expression.Parameters, midValues: midValues);
                        if (exp2.NodeType == ExpressionType.MemberAccess) list.Add(_val);
                        else list.Add($"{_val} {this.AddQuote(binding.Member.Name)}");
                    }
                    return list.ToStringSeparated(",");
                }
            }
            var sql = BuilderHelper.ParseSql(exp, this, midValues: midValues);
            return sql;
        }

        public string ColumnSeg<TEntity>(Expression<Func<TEntity, object>> expression = null) where TEntity : class, new()
        {
            var entity = GetEntityInfoInternal(typeof(TEntity), false);
            if (expression == null || expression.Body == expression.Parameters.FirstOrDefault())
            {
                //ColumnSeg<UserEntity>() 或 ColumnSeg<UserEntity>(i=>i)
                var alias = expression?.Parameters.FirstOrDefault()?.Name;
                return entity.EntityPropertyInfos
                    .Where(i => i.IsColumn && !i.IsIgnoreSelect)
                    .OrderBy(i => i.Order)
                    .Select(i => i.SelectFunc((alias.IsNullOrEmptyOrWhiteSpace() || alias == "_") ? "" : alias))
                    .ToStringSeparated(",");
            }
            var (exp, midValues) = ExpressionHelper.ReduceLambda(expression);
            var sql = BuilderHelper.ParseSql(exp, this, midValues: midValues);
            return sql;
        }
        public string ColumnSeg<TEntity1, TEntity2>(Expression<Func<TEntity1, TEntity2, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var sql = BuilderHelper.ParseSql(expression, this);
            return sql;
        }
        public string ColumnSeg<TEntity1, TEntity2, TEntity3>(Expression<Func<TEntity1, TEntity2, TEntity3, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var sql = BuilderHelper.ParseSql(expression, this);
            return sql;
        }
        public string ColumnSeg<TEntity1, TEntity2, TEntity3, TEntity4>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
            where TEntity4 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var sql = BuilderHelper.ParseSql(expression, this);
            return sql;
        }
        public string ColumnSeg<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, object>> expression)
           where TEntity1 : class, new()
           where TEntity2 : class, new()
           where TEntity3 : class, new()
           where TEntity4 : class, new()
           where TEntity5 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var sql = BuilderHelper.ParseSql(expression, this);
            return sql;
        }
        public string ColumnSeg<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, TEntity6>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, TEntity6, object>> expression)
           where TEntity1 : class, new()
           where TEntity2 : class, new()
           where TEntity3 : class, new()
           where TEntity4 : class, new()
           where TEntity5 : class, new()
           where TEntity6 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            var sql = BuilderHelper.ParseSql(expression, this);
            return sql;
        }
        #endregion
        #region WhereSeg
        private string Where(LambdaExpression expression, string[] prefixes, bool[] enum2Strings)
        {
            ArgumentNullException.ThrowIfNull(expression, nameof(expression));
            var (exp, midValues) = ExpressionHelper.ReduceLambda(expression);
            var res = BuilderHelper.GetConstant(exp, midValues);
            if (res.Success)
            {
                var b = res.Data.To<bool>();
                return b ? "1=1" : "1=0";
            }
            var lambda = exp as LambdaExpression;
            var aliases = new List<KeyValuePair<ParameterExpression, string>>();
            var parameters = new List<ParameterExpression>();
            var enum2StringsMap = new Dictionary<ParameterExpression, bool>();
            var idx = 0;
            foreach (var para in lambda.Parameters)
            {
                var type = para.Type;
                var typePure = type.IsNullable() ? type.GenericTypeArguments[0] : type;
                if (idx < (prefixes?.Length ?? 0) && prefixes[idx].IsNotNullOrEmptyOrWhiteSpace())
                {
                    aliases.Add(new KeyValuePair<ParameterExpression, string>(para, prefixes[idx] + "." + para.Name));
                }
                if (idx < (enum2Strings?.Length ?? 0)) enum2StringsMap.Add(para, enum2Strings[idx]);
                parameters.Add(para);
                idx++;
            }
            return BuilderHelper.ParseSql(expression: lambda, db: this, aliasesMap: aliases, parameters: parameters, midValues: midValues, enum2StringMap: enum2StringsMap);
        }
        private string WhereIf(bool isTrue, LambdaExpression expression, string[] prefixs, bool[] enum2Strings)
        {
            if (!isTrue) return string.Empty;
            return Where(expression, prefixs, enum2Strings);
        }
        /// <summary>
        /// where构建器,示例:
        /// <list type="number">
        /// <item>db.WhereSeg&lt;string>(name => name.Contains("小明")) => where name like '%小明%'</item>
        /// <item>db.WhereSeg&lt;int>(id => id==1) => where id = 1</item>
        /// <item>mysql: db.WhereSeg&lt;PersonEntity>(p => p.Birth >= DateTime.Parse("1990-01-01")) => where p.birth > '1990-01-01 00:00:00.000'</item>
        /// </list>
        /// </summary>
        public string WhereSeg<T>(Expression<Func<T, bool>> expression, string prefix = null, bool? enum2String = null) => Where(expression, [prefix], enum2String == null ? [] : [enum2String.Value]);
        public string WhereSegIf<T>(bool isTrue, Expression<Func<T, bool>> expression, string prefix = null, bool? enum2String = null) => WhereIf(isTrue, expression, [prefix], enum2String == null ? [] : [enum2String.Value]);
        public string WhereSeg<T, T2>(Expression<Func<T, T2, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => Where(expression, prefixs, enum2Strings);
        public string WhereSegIf<T, T2>(bool isTrue, Expression<Func<T, T2, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => WhereIf(isTrue, expression, prefixs, enum2Strings);
        public string WhereSeg<T, T2, T3>(Expression<Func<T, T2, T3, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => Where(expression, prefixs, enum2Strings);
        public string WhereSegIf<T, T2, T3>(bool isTrue, Expression<Func<T, T2, T3, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => WhereIf(isTrue, expression, prefixs, enum2Strings);
        public string WhereSeg<T, T2, T3, T4>(Expression<Func<T, T2, T3, T4, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => Where(expression, prefixs, enum2Strings);
        public string WhereSegIf<T, T2, T3, T4>(bool isTrue, Expression<Func<T, T2, T3, T4, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => WhereIf(isTrue, expression, prefixs, enum2Strings);
        #endregion
        #region AndSeg
        private string And(LambdaExpression expression, string[] prefixs, bool[] enum2Strings)
        {
            var sql = Where(expression, prefixs, enum2Strings);
            return $"and {sql}";
        }
        private string AndIf(bool isTrue, LambdaExpression expression, string[] prefixs, bool[] enum2String)
        {
            if (!isTrue) return string.Empty;
            return And(expression, prefixs, enum2String);
        }
        public string AndSeg<T>(Expression<Func<T, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T>(bool isTrue, Expression<Func<T, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2>(Expression<Func<T, T2, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2>(bool isTrue, Expression<Func<T, T2, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3>(Expression<Func<T, T2, T3, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3>(bool isTrue, Expression<Func<T, T2, T3, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4>(Expression<Func<T, T2, T3, T4, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4>(bool isTrue, Expression<Func<T, T2, T3, T4, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5>(Expression<Func<T, T2, T3, T4, T5, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5, T6>(Expression<Func<T, T2, T3, T4, T5, T6, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5, T6>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, T6, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5, T6, T7>(Expression<Func<T, T2, T3, T4, T5, T6, T7, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5, T6, T7>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, T6, T7, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5, T6, T7, T8>(Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5, T6, T7, T8>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, T9, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5, T6, T7, T8, T9>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, T9, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        public string AndSeg<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => And(expression, prefixs, enum2Strings);
        public string AndSegIf<T, T2, T3, T4, T5, T6, T7, T8, T9, T10>(bool isTrue, Expression<Func<T, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> expression, string[] prefixs = null, bool[] enum2Strings = null) => AndIf(isTrue, expression, prefixs, enum2Strings);
        #endregion
        #region CaseSeg
        /// <summary>
        /// <code>
        /// select case t.type when 1 then 'A' when 2 then 'B' else 'C'end grade
        /// from(select 1 type) t
        /// </code>
        /// <seealso href="https://dev.mysql.com/doc/refman/8.0/en/flow-control-functions.html#operator_case"/>
        /// </summary>
        public CaseSegBuilder CaseSeg(string colName)
        {
            Ensure.NotNullOrEmptyOrWhiteSpace(colName, nameof(colName));
            return new CaseSegBuilder(this, colName);
        }
        public CaseSegBuilder CaseSeg<T>(Expression<Func<T, object>> expression)
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }
        public CaseListSegBuilder<T> CaseListSeg<T>(IEnumerable<T> list, Expression<Func<T, object>> whenExp, Expression<Func<T, object>> thenExp) where T : class, new()
        {
            Ensure.NotNull(whenExp, nameof(whenExp));
            Ensure.NotNull(thenExp, nameof(thenExp));
            return new CaseListSegBuilder<T>(this, list, whenExp, thenExp);
        }
        public CaseSegBuilder CaseSeg<TEntity1, TEntity2>(Expression<Func<TEntity1, TEntity2, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }

        public CaseSegBuilder CaseSeg<TEntity1, TEntity2, TEntity3>(Expression<Func<TEntity1, TEntity2, TEntity3, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }
        public CaseSegBuilder CaseSeg<TEntity1, TEntity2, TEntity3, TEntity4>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
            where TEntity4 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }
        public CaseSegBuilder CaseSeg<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
            where TEntity4 : class, new()
            where TEntity5 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }
        public CaseSegBuilder CaseSeg<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, TEntity6>(Expression<Func<TEntity1, TEntity2, TEntity3, TEntity4, TEntity5, TEntity6, object>> expression)
            where TEntity1 : class, new()
            where TEntity2 : class, new()
            where TEntity3 : class, new()
            where TEntity4 : class, new()
            where TEntity5 : class, new()
            where TEntity6 : class, new()
        {
            Ensure.NotNull(expression, nameof(expression));
            return new CaseSegBuilder(this, expression);
        }

        /// <summary>
        /// <code>
        /// select case  when t.score > 90 then '优'  when t.score > 80 then '良'  when t.score > 60 then '中'  else '差'end grade
        /// from(select 95 score) t
        /// </code>
        /// <seealso href="https://dev.mysql.com/doc/refman/8.0/en/flow-control-functions.html#operator_case"/>
        /// </summary>
        public CaseSegBuilder CaseSeg() => new(this);
        #endregion
        #endregion

        #region 发布变动通知
        /// <summary>
        /// 手动触发变动通知,示例:
        /// <code>
        /// //使用 ExecuteSql 更新表数据
        /// db.ExecuteSql("update t_user set name='小明' where id=1");
        /// 
        /// //手动触发变动通知
        /// db.PublishWriteChange(new AfterWriteArgument{ TableName="t_user",WriteType=EnumWriteType.Update,EntityInfo=null });
        /// </code>
        /// </summary>
        public void PublishWriteChange(AfterWriteArgument arg)
        {
            if (!Setting.HasAfterWriteMonitor) return;
            Ensure.NotNull(arg.TableName, nameof(arg.TableName));
            Setting.AfterWriteMonitorAction(arg);
        }
        #endregion

        #region hook
        private readonly Dictionary<string, Func<object, object>> _hooks = [];
        private readonly Dictionary<string, Func<object, object>> _prefixHooks = [];
        protected void InjectHook(string name, Func<object, object> func)
        {
            if (name.StartsWith("prefix:"))
            {
                _prefixHooks[name.Substring("prefix:".Length)] = func;
            }
            else
            {
                _hooks[name] = func;
            }
        }
        internal Func<object, object> GetHook(string name)
        {
            if (_hooks.TryGetValue(name, out Func<object, object> value)) return value;
            return null;
        }
        internal Func<object, object> GetPrefixMatchHook(string name)
        {
            var hook = _prefixHooks.FirstOrDefault(i => name.StartsWith(i.Key));
            return hook.Value;
        }
        #endregion

        #region ConvertToSqlSeg ParseJsonDataType ConvertJsonLiteralToSql ConvertJsonVariableToSql
        private static readonly List<Type> types =
        [
            typeof(sbyte),
            typeof(byte),
            typeof(short),
            typeof(ushort),
            typeof(int),
            typeof(uint),
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double),
            typeof(decimal)
        ];
        /// <summary>
        /// 将任意对象转为可拼接的sql,示例:
        /// <list type="bullet">
        /// <item>字符串: db.ConvertToSqlSeg("刘备") => '刘备'</item>
        /// <item>原始字符串: db.ConvertToSqlSeg(new RawString("user()")) => user()</item>
        /// <item>日期: db.ConvertToSqlSeg(DateTime.Now) => '2023-01-19 01:02:03.123'</item>
        /// <item>日期带格式: db.ConvertToSqlSeg(DateTime.Now,"yyyy-MM-dd") => '2023-01-19'</item>
        /// <item>日期带时区: sqlserver: db.ConvertToSqlSeg(DateTimeOffset.Now) => '2023-01-19 01:02:03.1234567 +08:00'</item>
        /// <item>日期带时区: mysql: db.ConvertToSqlSeg(DateTimeOffset.Now) => '2023-01-19 01:02:03.1234567+08:00'</item>
        /// <item>GUID: db.ConvertToSqlSeg(Guid.NewGuid()) => '10adf41f2e974a289025b5355d473eca'</item>
        /// <item>GUID带格式: db.ConvertToSqlSeg(Guid.NewGuid(),"D") => '10adf41f-2e97-4a28-9025-b5355d473eca'</item>
        /// <item>字节数组: db.ConvertToSqlSeg(new byte[] { 0x12, 0x13 }) => 0x1213</item>
        /// <item>数组集合: db.ConvertToSqlSeg(new[] { 1, 2, 3 }) => (1,2,3)</item>
        /// <item>元祖: db.ConvertToSqlSeg((1, 2)) => (1,2)</item>
        /// </list>
        /// 支持的参数形式:
        /// <list type="number">
        /// <item>guid: "N/D/B/P/X"</item>
        /// <item>enum: true/false 表示是否将枚举转为字符串</item>
        /// <item>datetime: "yyyy-MM-dd HH:mm:ss.fff"</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: guid默认转换为 "10adf41f2e974a289025b5355d473eca" 如果想中间带下划线,使用: db.ConvertToSqlSeg(guid,"D") => "10adf41f-2e97-4a28-9025-b5355d473eca" </remarks>
        public virtual Result<string> ConvertToSqlSeg(object obj, object args = null)
        {
            //简单类型
            if (obj == null || obj is DBNull) return Result.Ok("null");
            if (obj is string str) return Result.Ok($"{ProtectString(str)}");
            if (obj is char c) return Result.Ok($"{ProtectString(c + "")}");
            if (obj is RawString rawStr) return Result.Ok($"({rawStr.String.TrimEnd(';')})");
            if (obj is bool b) return Result.Ok(b ? "1" : "0");

            //数字
            var type = obj.GetType();
            if (types.Contains(type)) return Result.Ok(obj.ToString());

            //枚举类型
            if (type.IsEnum)
            {
                //将枚举转为字符串
                if (args is bool flag) if (flag) return Result.Ok($"'{obj}'");
                return Result.Ok(obj.To<int>().ToString());
            }

            //guid
            if (obj is Guid guid)
            {
                if (args is string s) return Result.Ok($"'{guid.ToString(s)}'");
                return Result.Ok($"'{guid:N}'");
            }

            //datetime
            //sqlserver的datetime小数位数3,超过将报错 而不是截断 datetime2会自动截断
            //mysql 最多支持6位小数 超出的自动截断
            if (obj is DateTime dateTime)
            {
                string seg = null;
                if (args is string s) seg = dateTime.ToString(s);
                else seg = dateTime.ToCommonStampString();
                seg = seg.TrimEnd('0').TrimEnd('.');
                if (seg.EndsWith(" 00:00:00")) seg = seg.Substring(0, seg.Length - " 00:00:00".Length);
                return Result.Ok($"'{seg}'");
            }

            //datetimeoffset sqlserver的datetimeoffset最大精度7位,超过会自动截断
            if (obj is DateTimeOffset dateTimeOffset)
            {
                var seg = dateTimeOffset.ToString("yyyy-MM-dd HH:mm:ss.fffffff").TrimEnd('0').Trim('.');
                if (seg.EndsWith(" 00:00:00")) seg = seg.Substring(0, seg.Length - " 00:00:00".Length);
                return Result.Ok($"'{seg} {dateTimeOffset:zzz}'");
            }

            //timespan
            if (obj is TimeSpan timeSpan) return Result.Ok($"'{timeSpan}'");
            if (obj is DateOnly dateOnly) return Result.Ok($"'{dateOnly:yyyy-MM-dd}'");
            if (obj is TimeOnly timeOnly) return Result.Ok($"'{timeOnly:HH:mm:ss.fff}'");
            #region 二进制
            if (obj is IEnumerable<byte> bytes)
            {
                var sb = new StringBuilder();
                sb.Append("0x");
                foreach (var by in bytes) sb.Append(by.ToString("X2"));
                return Result.Ok(sb.ToString());
            }
            if (obj is IEnumerable<sbyte> sbytes)
            {
                var sb = new StringBuilder();
                sb.Append("0x");
                foreach (var by in sbytes) sb.Append(by.ToString("X2"));
                return Result.Ok(sb.ToString());
            }
            #endregion            

            #region 数字集合或数组
            //先处理枚举数组 因为它可能被识别成 IEnumerable<int>
            if (args is true && obj is IEnumerable _arr)
            {
                Type _type = null;
                foreach (var item in _arr)
                {
                    _type = item?.GetType();
                    if (_type != null) break;
                }
                if (_type != null && (_type.IsEnum || Nullable.GetUnderlyingType(_type)?.IsEnum == true))
                {
                    var _sb = new StringBuilder();
                    _sb.Append('(');
                    var idx = 0;
                    foreach (var item in _arr)
                    {
                        if (idx > 0) _sb.Append(',');
                        _sb.Append($"'{item.ToString()}'");
                        idx++;
                    }
                    _sb.Append(')');
                    return Result.Ok(_sb.ToString());
                }
            }
            if (obj is IEnumerable<short> li_short) return Result.Ok($"({li_short.ToStringSeparated(",")})");
            if (obj is IEnumerable<ushort> li_ushort) return Result.Ok($"({li_ushort.ToStringSeparated(",")})");
            if (obj is IEnumerable<int> li_int) return Result.Ok($"({li_int.ToStringSeparated(",")})");
            if (obj is IEnumerable<uint> li_uint) return Result.Ok($"({li_uint.ToStringSeparated(",")})");
            if (obj is IEnumerable<long> li_long) return Result.Ok($"({li_long.ToStringSeparated(",")})");
            if (obj is IEnumerable<ulong> li_ulong) return Result.Ok($"({li_ulong.ToStringSeparated(",")})");
            if (obj is IEnumerable<float> li_float) return Result.Ok($"({li_float.ToStringSeparated(",")})");
            if (obj is IEnumerable<double> li_double) return Result.Ok($"({li_double.ToStringSeparated(",")})");
            if (obj is IEnumerable<decimal> li_decimal) return Result.Ok($"({li_decimal.ToStringSeparated(",")})");
            if (obj is IEnumerable<string> li_string) return Result.Ok($"({li_string.Select(i => $"{ProtectString(i)}").ToStringSeparated(",")})");
            if (obj is IEnumerable<object> li_object) return Result.Ok($"({li_object.Select(i => ConvertToSqlSeg(i, args).UnWrap()).ToStringSeparated(",")})");
            if (obj is IEnumerable li_object2)
            {
                var li = new List<string>();
                foreach (var item in li_object2) li.Add(ConvertToSqlSeg(item, args).UnWrap());
                return Result.Ok($"({li.ToStringSeparated(",")})");
            }
            #endregion

            #region 元组
            if (obj is ValueTuple<int, int> valueTuple) return Result.Ok($"({valueTuple.Item1},{valueTuple.Item2})");
            if (obj is ValueTuple<long, long> valueTuple2) return Result.Ok($"({valueTuple2.Item1},{valueTuple2.Item2})");
            var typeDesc = type.GetClassGenericFullName();
            if (typeDesc.Name.StartsWith("System.ValueTuple<"))
            {
                var li = new List<string>();
                var accessor = Accessor.Build(type);
                for (int i = 0; i < typeDesc.GenericTypes.Count; i++)
                {
                    li.Add(ConvertToSqlSeg(accessor[obj, "Item" + (i + 1)]).UnWrap());
                }
                return Result.Ok($"({li.ToStringSeparated(",")})");
            }
            #endregion

            return Result.NotOk($"未能转换 {obj.GetType().FullName} 类型数据到sql!");
        }

        /// <summary>
        /// 将表达式解析为某一种JsonDataType,与 <seealso cref="ConvertJsonVariableToSql"/> 配合使用
        /// </summary>
        public virtual EnumJsonDataType ParseJsonDataType(Expression exp)
        {
            if (exp.Type == typeof(object) && exp.NodeType == ExpressionType.Convert)
                return ParseJsonDataType((exp as UnaryExpression).Operand);
            return ParseJsonDataType(exp.Type);
        }

        /// <summary>
        /// 将 <c>Type</c> 解析为某一种JsonDataType,与 <seealso cref="ConvertJsonVariableToSql"/> 配合使用
        /// </summary>
        public virtual EnumJsonDataType ParseJsonDataType(Type type)
        {
            if (type.IsNullable()) type = Nullable.GetUnderlyingType(type);
            if (type.IsAssignableTo(typeof(string))) return EnumJsonDataType.String;

            if (type.IsAssignableTo(typeof(IEnumerable)))
            {
                if (type == typeof(JsonObject)) return EnumJsonDataType.Object;
                var reflect = type.GetClassGenericFullName();
                if (reflect.Name == "Newtonsoft.Json.Linq.JObject"
                    || reflect.Name == "System.Collections.Generic.Dictionary<TKey, TValue>"
                    || reflect.Name == "System.Collections.Generic.IDictionary<TKey, TValue>"
                    )
                    return EnumJsonDataType.Object;
                return EnumJsonDataType.Array;
            }
            if (type.IsNumeric() || type.IsEnum) return EnumJsonDataType.Number;
            if (type == typeof(bool)) return EnumJsonDataType.Bool;
            //typeof(MySqlConnector.MySqlDateTime)//不考虑 MySqlDateTime.ToJson() 形式为 {"":""...}
            if (type.IsAssignableTo(typeof(string))
                || type == typeof(Guid)
                || type == typeof(DateTime)
                || type == typeof(DateTime)
                || type == typeof(DateTimeOffset)
                || type == typeof(DateOnly)
                || type == typeof(TimeOnly)
                || type == typeof(TimeSpan)
                || type.GetTypeCode() == TypeCode.DateTime
                ) return EnumJsonDataType.String;
            return EnumJsonDataType.Object;
        }

        /// <summary>
        /// 不同的位置,需要不同的写法,比如:
        /// <list type="number">
        /// <item>[JsonStore(Bucket="properties")] public string Name{ get; set; }
        /// <list type="bullet">
        ///     <item>set properties = 'tom' //err</item>
        ///     <item>set properties = '"tom"' //success</item>
        ///     <item>set properties = concat('a','b') //err</item>
        ///     <item>set properties = json_quote(concat('a','b')) success</item>
        /// </list>
        /// </item>
        /// <item>[JsonStore(Bucket="properties")] public bool Flag{ get; set; }
        /// <list type="bullet">
        /// <item>set properties = true //err</item>
        /// <item>set properties = 'true' //success</item>
        /// <item>set properties = 1>0 //err</item>
        /// <item>set properties = cast(1>0 as json) success</item>
        /// </list>
        /// </item>
        /// <item>想创建一个数组,里面只有对象 {"age":18}
        /// <list type="bullet">
        /// <item>select json_array('{"age":18}') //err 创建的数组里面是字符串 ["{\"age\":18}"]</item>
        /// <item>select json_array(cast('{"age":18}' as json)) //success [{"age": 18}]</item>
        /// </list>
        /// </item>
        /// <item>所以,将json分为 JsonDoc 和 JsonValue
        /// <list type="bullet">
        /// <item>set properties = 接受的是 JsonDoc</item>
        /// <item>json_array 接受 JsonValue,返回 JsonDoc</item>
        /// <item>json_merge_patch 接受的是 JsonDoc 返回的是 JsonDoc</item>
        /// <item>json_extract 接受的是 JsonDoc 返回的是 JsonDoc</item>
        /// <item>json_value 接受的是 JsonDoc 返回的是 JsonValue</item>
        /// </list>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="inst">常量</param>
        /// <param name="destType">目标位置</param>
        public virtual (string res, EnumJsonDataType type) ConvertJsonLiteralToSql(object inst, EnumJsonAcceptAsType destType)
        {
            //不能 ignorenull, 因为不知道用到哪里, 如果是 json_merge_patch 还指望 null 值去将原来的擦除呢
            //如:
            //select json_merge_patch('{"detail":{"age":20}}','{"name":"jack","detail":null}')  //out: {"name": "jack"}
            //select json_merge_patch('{"detail":{"age":20}}','{"name":"jack"}')  //out: {"name": "jack", "detail": {"age": 20}}
            var json = inst.ToJsonFast(otherSettings: options =>
            {
                options.TypeInfoResolver = new DefaultJsonTypeInfoResolver
                {
                    Modifiers = { (typeInfo) =>
                    {
                        for (int i = 0; i < typeInfo.Properties.Count; i++)
                        {
                            var property = typeInfo.Properties[i];
                            var prop = property.AttributeProvider as PropertyInfo;
                            if (prop != null) property.Name = prop.Name;
                        }
                    } }
                };
            });
            if (json == null) return ("null", EnumJsonDataType.Null);
            if (json.StartsWith('{') || json.StartsWith('['))
            {
                //object or array
                /* 假设: obj = new { name = 
                 * """
                 * "
                 * '
                 * """}
                 * ToJson 的返回结果如: {"name":"\"\r\n'"} 此时,它还不能参与sql拼接,如:
                 * select cast('{"name":"\"\r\n'"}' as json) -- 语法直接报错
                 * select cast('{"name":"\"\r\n"}' as json) -- 去掉单引号后执行报错
                 * 正确的应该是: select cast('{\"name\":\"\\\"\\r\\n\'\"}' as json)
                 * 即, 将 ToJson() 的返回再进行一次转义
                 */
                var dataType = json.StartsWith('{') ? EnumJsonDataType.Object : EnumJsonDataType.Array;
                if (destType == EnumJsonAcceptAsType.Value)
                    //return cast('[1,2]' as json)
                    return ($"cast({ProtectString(json)} as json)", dataType);
                else if (destType == EnumJsonAcceptAsType.Doc)
                    //return '[1,2]'
                    return (ProtectString(json), dataType);
                else
                    return (json, dataType);
            }
            else if (json.StartsWith('\"'))
            {
                // string
                // ToJson 的返回结果如: "name", "na\"me'" 此时,它还不能参与sql拼接,如:
                //string json = "na\"me" => json_array('na\"me') ...
                if (destType == EnumJsonAcceptAsType.Value)
                    //return 'tom'
                    return ('\'' + json.Substring(1, json.Length - 2).Replace("'", "\\'") + '\'', EnumJsonDataType.String);
                else if (destType == EnumJsonAcceptAsType.Doc)
                    //return '"tom"'
                    return (ProtectString(json), EnumJsonDataType.String);
                else
                    return (json, EnumJsonDataType.String);
            }
            else if (json == "true" || json == "false")
            {
                //bool
                if (destType == EnumJsonAcceptAsType.Value)
                    //return true
                    return (json, EnumJsonDataType.Bool);
                else if (destType == EnumJsonAcceptAsType.Doc)
                    //return 'true'
                    return ($"'{json}'", EnumJsonDataType.Bool);
                else
                    return (json, EnumJsonDataType.Bool);
            }
            else
            {
                //number
                if (destType == EnumJsonAcceptAsType.Doc)
                    //return cast(1 as json)
                    return ($"cast({json} as json)", EnumJsonDataType.Number);
                else if (destType == EnumJsonAcceptAsType.Value)
                    //return 1
                    return (json, EnumJsonDataType.Number);
                else
                    return (json, EnumJsonDataType.Number);
            }
        }

        /// <summary>
        /// 不同的位置,需要不同的写法,比如:
        /// <list type="number">
        /// <item>[JsonStore(Bucket="properties")] public string Name{ get; set; }
        /// <list type="bullet">
        ///     <item>set properties = 'tom' //err</item>
        ///     <item>set properties = '"tom"' //success</item>
        ///     <item>set properties = concat('a','b') //err</item>
        ///     <item>set properties = json_quote(concat('a','b')) success</item>
        /// </list>
        /// </item>
        /// <item>[JsonStore(Bucket="properties")] public bool Flag{ get; set; }
        /// <list type="bullet">
        /// <item>set properties = true //err</item>
        /// <item>set properties = 'true' //success</item>
        /// <item>set properties = 1>0 //err</item>
        /// <item>set properties = cast(1>0 as json) success</item>
        /// </list>
        /// </item>
        /// <item>想创建一个数组,里面只有对象 {"age":18}
        /// <list type="bullet">
        /// <item>select json_array('{"age":18}') //err 创建的数组里面是字符串 ["{\"age\":18}"]</item>
        /// <item>select json_array(cast('{"age":18}' as json)) //success [{"age": 18}]</item>
        /// </list>
        /// </item>
        /// <item>所以,将json分为 JsonDoc 和 JsonValue
        /// <list type="bullet">
        /// <item>set properties = 接受的是 JsonDoc</item>
        /// <item>json_array 接受 JsonValue,返回 JsonDoc</item>
        /// <item>json_merge_patch 接受的是 JsonDoc 返回的是 JsonDoc</item>
        /// <item>json_extract 接受的是 JsonDoc 返回的是 JsonDoc</item>
        /// <item>json_value 接受的是 JsonDoc 返回的是 JsonValue</item>
        /// </list>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="seg">sqlseg</param>
        /// <param name="srcType">原json类型</param>
        /// <param name="destType">目标位置</param>
        public abstract string ConvertJsonVariableToSql(string seg, EnumJsonDataType srcType, EnumJsonAcceptAsType destType);
        #endregion
    }
}